CSS Charts: How to Create an Organizational Chart
1 month ago · Updated 1 month ago

CSS is more powerful than many developers give it credit for. While JavaScript libraries like D3.js, Highcharts, and Chart.js dominate discussions about data visualization on the web, a surprising range of chart types can be built with nothing more than HTML structure and thoughtfully applied CSS. This tutorial series has already explored bar charts, thermometer charts, and donut charts all built without a single line of JavaScript. Today we continue that journey with one of the most practically useful chart types in web design: the organizational chart.
The organizational chart also called an organigram or organogram is a diagram that shows the structure of an organization and the relationships and relative ranks of its parts, positions, and jobs. On corporate websites, you will almost always find it on the "About Us" or "Company" page. It is also the structure used to display family trees, biological taxonomies, file system hierarchies, and any other data that has a tree-like parent-child structure.
Building this chart in pure CSS is genuinely challenging. The connecting lines that visually represent the relationships between nodes cannot be drawn with simple border properties; they require careful use of CSS pseudo-elements (::before and ::after), precise absolute positioning, and some creative use of CSS Grid for the layout. Achieving responsive behavior on small screens requires additional thought about how the hierarchy should be restructured when horizontal space is limited.
This comprehensive tutorial walks you through every step of building a four-level CSS organizational chart from scratch. We will cover the HTML structure, the CSS for each level, the technique for drawing connecting lines with pseudo-elements, the responsive layout strategy, and the broader context of organizational charts as a design pattern. By the end, you will have a reusable, responsive, CSS-only organizational chart component that you can adapt for any project.
"Without writing a single line of JavaScript, we managed to build a fully functional organizational chart." — George Martsoukos
What Is an Organizational Chart?
Before we write any code, it is worth taking a moment to understand what organizational charts are, where they come from, and how they are used in the real world. This context will help you make better design decisions as you build and customize your own version.
The Wikipedia Definition
Wikipedia provides a clear and useful definition that captures the scope of the concept: an organizational chart, also called an organigram or organogram, is a diagram that shows the structure of an organization and the relationships and relative ranks of its parts and positions or jobs. The term is also used for similar diagrams, for example ones showing the different elements of a field of knowledge or a group of languages.
This definition emphasizes two key aspects: structure and relationships. An organizational chart is not just a list of people or roles; it is a visual representation of how those people or roles relate to each other, specifically in terms of hierarchy, authority, and reporting structure. The visual encoding of hierarchy higher-ranking nodes near the top, lower-ranking nodes near the bottom, with connecting lines showing who reports to whom is what makes organizational charts so instantly readable and useful.
Common Use Cases for Organizational Charts
Organizational charts appear in a wider variety of contexts than most developers might initially think. Understanding these use cases will help you design your chart component to be appropriately flexible.
- Corporate hierarchy diagrams: The classic use case, showing the chain of command from executive leadership down through departments, teams, and individual contributors. These are found on corporate websites, in annual reports, in employee handbooks, and in investor presentations.
- Family trees and genealogy: Organizational chart structure is perfectly suited to representing family relationships across generations. The British Royal Family's line of succession, as displayed on the BBC's website, is a well-known example of organizational chart structure applied to genealogy.
- Academic and knowledge taxonomies: Organizational charts can show how fields of knowledge are related — for example, how different branches of mathematics relate to each other, or how programming languages are organized into families and subsets.
- File system and software architecture diagrams: The hierarchical structure of file systems (folders containing subfolders containing files) maps naturally onto organizational chart structure, as does the component hierarchy in frameworks like React or the module structure of large software projects.
- Government and institutional structures: Governmental organizational charts show how ministries, departments, and agencies relate to each other and to the central executive. These are commonly published as public information documents.
![]()
Figure 2: An example organizational chart showing the classic top-down hierarchical structure used for corporate or institutional organization diagrams. The highest-ranking position (CEO or equivalent) sits at the top, with reporting relationships shown by connecting lines descending to progressively lower levels. The visual conventions of this format — boxes, lines, and top-to-bottom hierarchy — are universally recognized and immediately interpretable. (Credit: S.s.kulkarni9090, CC BY-SA 3.0 / Wikimedia Commons)
Why Build This in Pure CSS?
Given that JavaScript libraries like Highcharts.js can generate organizational charts dynamically from data, you might reasonably ask why anyone would go to the trouble of building one in pure CSS. There are several good reasons.
Performance is the most compelling argument. A pure CSS chart has no JavaScript runtime overhead, no library to parse and execute, no DOM manipulation to perform. The browser simply renders HTML and applies CSS, which is what it is optimized to do. For a static chart that does not need to update dynamically, pure CSS is the most lightweight possible implementation.
Maintainability and simplicity are also significant advantages. A CSS chart is pure markup and style — any developer who knows HTML and CSS can understand and modify it, without needing to know the API of a specific JavaScript library. This reduces the learning curve for maintenance and makes the component more portable across projects.
The educational value of building CSS charts should not be overlooked either. The techniques required — pseudo-elements for drawing lines, CSS Grid for layout, absolute positioning for precise placement — are all fundamental CSS skills that transfer to many other development contexts. Building a CSS chart is an excellent way to deepen your understanding of these techniques.
Finally, there is the constraint of context. In situations where JavaScript is unavailable or undesirable — strict content security policies, AMP pages, email templates, or server-side rendering environments that need to work without client-side JavaScript — a pure CSS solution is the only option.
Setting Up: Container and Base Styles
Every well-built CSS component starts with a solid foundation of reset rules, custom properties, and base styles. These establish the visual constants — colors, spacing, fonts — that will be used consistently throughout the component, and prevent browser default styles from interfering with the layout.
The Container
Our organizational chart lives inside a container element that constrains its maximum width and centers it on the page. This is standard practice for any full-width layout component:
HTML
<div class="container"> <!-- Chart goes here --> </div>
The container div serves as the bounding box for the entire chart. By setting a maximum width and using margin: 0 auto, we ensure the chart is centered within the viewport and does not stretch uncomfortably wide on large screens.
CSS Custom Properties (Variables)
We define four color variables — one for each chart level — using CSS custom properties. This makes it trivial to retheme the chart by changing just four values, rather than hunting through the stylesheet for every color reference:
CSS — Custom Properties and Base Reset
:root { --level-1: #8dccad; /* Soft green — Level 1 */ --level-2: #f5cc7f; /* Warm yellow — Level 2 */ --level-3: #7b9fe0; /* Soft blue — Level 3 */ --level-4: #f27c8d; /* Soft pink — Level 4 */ --black: black; /* Line color */ } * { padding: 0; margin: 0; box-sizing: border-box; } ol { list-style: none; } body { margin: 50px 0 100px; text-align: center; font-family: "Inter", sans-serif; } .container { max-width: 1000px; padding: 0 10px; margin: 0 auto; }
The Rectangle Class
Every node in the chart — regardless of its level — uses the rectangle class for its base visual appearance. This establishes the card-like appearance with padding and a subtle box shadow:
CSS — Rectangle (shared node style)
.rectangle { position: relative; padding: 20px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); }
The position: relative declaration on the rectangle class is critically important. Every pseudo-element that draws the connecting lines between nodes uses position: absolute, and absolute positioning is relative to the nearest positioned ancestor. By making every rectangle relatively positioned, we ensure that pseudo-elements are positioned relative to their parent node rather than some more distant ancestor.
The box-shadow gives each node a subtle depth that helps them stand out from the background and from the connecting lines, creating a clear visual hierarchy between the nodes (foreground) and the connectors (background).
⚠️ Pro Tip: For simplicity in this tutorial, the CSS is intentionally not optimized for minimal redundancy. Some properties are repeated across levels to make each level's styles self-contained and easier to understand individually. In a production implementation, you would consolidate shared properties into shared classes.
Building the Four Chart Levels
With the foundation in place, we can build each level of the chart. The chart has four levels, each representing a tier of organizational hierarchy. Level 1 is the topmost node (e.g., the CEO), Level 2 contains two nodes (e.g., two C-level executives), Level 3 contains four nodes (e.g., department heads), and Level 4 contains sixteen nodes (e.g., team leads or individual contributors).
The key technical challenge at each level is twofold: positioning the nodes correctly in a grid layout, and drawing the connecting lines between nodes and their parents. The connecting lines are drawn entirely using CSS pseudo-elements, which is the most interesting and nuanced part of the implementation.
![]()
Figure 3: The four-level structure of the organizational chart, showing the progression from a single root node at Level 1 through two nodes at Level 2, four nodes at Level 3, and sixteen leaf nodes at Level 4. Each level is color-coded — green for Level 1, yellow for Level 2, blue for Level 3, and pink for Level 4. The connecting lines between levels are drawn entirely using CSS pseudo-elements positioned relative to their parent nodes. (Credit: George Martsoukos / Envato Tuts+)
Level 1: The Root Node
Level 1 contains a single node — the top of the organizational hierarchy. Because it is the most important element semantically, we use an h1 element. This is good for both accessibility and SEO, as it communicates the primary heading of the chart section to screen readers and search engines.
HTML — Level 1
<h1 class="level-1 rectangle">CEO</h1>
The CSS for Level 1 centers the node horizontally, gives it the level-1 color from our custom property, and uses the ::before pseudo-element to draw the vertical connecting line that descends from Level 1 down toward Level 2:
CSS — Level 1
.level-1 { width: 50%; margin: 0 auto 40px; background: var(--level-1); } .level-1::before { content: ""; position: absolute; top: 100%; /* starts at the bottom of the node */ left: 50%; /* starts at horizontal center */ transform: translateX(-50%); width: 2px; height: 20px; background: var(--black); }
The ::before pseudo-element creates a 2px wide, 20px tall vertical line that begins at the bottom center of the Level 1 node and descends toward the Level 2 wrapper. The top: 100% places the pseudo-element immediately below the node, and the left: 50% with translateX(-50%) centers it precisely on the horizontal midpoint of the node.
This pattern — using a pseudo-element with absolute positioning to draw a connecting line from the bottom center of a node downward — will recur throughout the chart. Understanding this fundamental pattern is the key to understanding the entire connecting-lines system.
Level 2: Two Child Nodes
Level 2 contains two nodes, positioned side by side using CSS Grid. Each Level 2 node is the parent of two Level 3 nodes, so it needs a connecting line below it as well as a connecting line above it (connecting to Level 1).
The Level 2 wrapper uses CSS Grid with two equal columns to position the two nodes:
HTML — Level 2
<ol class="level-2-wrapper"> <li> <h2 class="level-2 rectangle">CTO</h2> </li> <li> <h2 class="level-2 rectangle">CFO</h2> </li> </ol>
CSS — Level 2 Wrapper
.level-2-wrapper { position: relative; display: grid; grid-template-columns: repeat(2, 1fr); } /* Horizontal line connecting the two level-2 nodes */ .level-2-wrapper::before { content: ""; position: absolute; top: -20px; left: 25%; /* starts at center of left column */ width: 50%; /* spans to center of right column */ height: 2px; background: var(--black); } /* Mobile-only: vertical sidebar line (hidden by default) */ .level-2-wrapper::after { display: none; content: ""; position: absolute; left: -20px; bottom: -20px; width: calc(100% + 20px); height: 2px; background: var(--black); }
The ::before pseudo-element of the level-2-wrapper draws the horizontal line that connects the vertical stems above the two Level 2 nodes. This horizontal line runs from the center of the left column (25% of the wrapper width) to the center of the right column (75% of the wrapper width), creating a width of 50%. This is the T-shaped connector that is characteristic of organizational chart diagrams.
CSS — Level 2 Nodes
.level-2-wrapper > li { position: relative; } /* Vertical stem from horizontal bar down to node */ .level-2-wrapper > li::before { content: ""; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); width: 2px; height: 20px; background: var(--black); } .level-2 { width: 70%; margin: 0 auto 40px; background: var(--level-2); } /* Vertical stem from node down to level-3 wrapper */ .level-2::before { content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); width: 2px; height: 20px; background: var(--black); }
💡 Key Concept: Each connecting line segment in the chart is drawn by a separate pseudo-element. The T-shaped connector between Levels 1 and 2 is composed of four pieces: the vertical stem from Level 1 (on .level-1::before), the horizontal bar (on .level-2-wrapper::before), and the vertical stem from the bar to each Level 2 node (on .level-2-wrapper > li::before). Understanding this decomposition is the key to being able to modify or extend the chart.
Level 3: Four Nodes in Two Groups
Level 3 contains four nodes, distributed in two groups of two — the first group as children of the first Level 2 node, the second group as children of the second Level 2 node. Each group is implemented as a nested ordered list inside its parent Level 2 list item.
HTML — Level 3 (one group shown)
<ol class="level-3-wrapper"> <li> <h3 class="level-3 rectangle">Engineering</h3> </li> <li> <h3 class="level-3 rectangle">Design</h3> </li> </ol>
CSS — Level 3
.level-3-wrapper { position: relative; display: grid; grid-template-columns: repeat(2, 1fr); grid-column-gap: 20px; width: 90%; margin: 0 auto; } /* Horizontal bar connecting level-3 nodes */ .level-3-wrapper::before { content: ""; position: absolute; top: -20px; left: calc(25% - 5px); width: calc(50% + 10px); height: 2px; background: var(--black); } /* Vertical stems from bar to each node */ .level-3-wrapper > li::before { content: ""; position: absolute; top: 0; left: 50%; transform: translate(-50%, -100%); width: 2px; height: 20px; background: var(--black); } .level-3 { margin-bottom: 20px; background: var(--level-3); }
The Level 3 wrapper uses the same CSS Grid plus pseudo-element pattern established at Level 2. The slight adjustments to the horizontal bar position (left: calc(25% - 5px) and width: calc(50% + 10px)) account for the grid-column-gap between the two columns, ensuring the bar extends to exactly align with the center of each node despite the gap.
![]()
Figure 4: A close-up view of the CSS pseudo-element connecting lines between Level 2 and Level 3, illustrating how the T-shaped connector is assembled from multiple separate pseudo-elements. The vertical stem from the Level 2 node is drawn by .level-2::before; the horizontal bar is drawn by .level-3-wrapper::before; and the two vertical stems descending from the bar to each Level 3 node are drawn by .level-3-wrapper > li::before. This decomposition into small, composable pieces is what makes the CSS drawing technique manageable. (Credit: George Martsoukos / Envato Tuts+)
Level 4: Sixteen Leaf Nodes
Level 4 is the deepest level and contains sixteen nodes in total, grouped as four lists of four — one list per Level 3 node. Unlike the previous levels, the Level 4 nodes are arranged vertically rather than horizontally, and they connect to their parent via a vertical sidebar line rather than a T-shaped horizontal connector.
HTML — Level 4 (one group of four shown)
<ol class="level-4-wrapper"> <li><h4 class="level-4 rectangle">Frontend Dev</h4></li> <li><h4 class="level-4 rectangle">Backend Dev</h4></li> <li><h4 class="level-4 rectangle">DevOps</h4></li> <li><h4 class="level-4 rectangle">QA Engineer</h4></li> </ol>
CSS — Level 4
.level-4-wrapper { position: relative; width: 80%; margin-left: auto; } /* Vertical sidebar line connecting all level-4 nodes */ .level-4-wrapper::before { content: ""; position: absolute; top: -20px; left: -20px; width: 2px; height: calc(100% + 20px); background: var(--black); } .level-4-wrapper li + li { margin-top: 20px; } .level-4 { font-weight: normal; background: var(--level-4); } /* Horizontal connector from sidebar to node */ .level-4::before { content: ""; position: absolute; top: 50%; left: 0%; transform: translate(-100%, -50%); width: 20px; height: 2px; background: var(--black); }
The Level 4 connecting line system works differently from the previous levels. Rather than a T-shaped connector with a horizontal bar and vertical stems, Level 4 uses a vertical sidebar line (drawn by .level-4-wrapper::before) that runs the full height of the list, plus a horizontal connector from the sidebar to each individual node (drawn by .level-4::before).
The vertical sidebar is positioned 20px to the left of the wrapper (left: -20px) and extends from 20px above the wrapper (top: -20px) through the full height of the wrapper (height: calc(100% + 20px)). This creates a continuous vertical line on the left side of all the Level 4 nodes. Each Level 4 node then has a 20px horizontal connector that extends from its left edge back to the sidebar line, completing the branching connector pattern.
Understanding the Pseudo-Element Line Drawing Technique
The most intellectually interesting aspect of this CSS organizational chart is the technique for drawing connecting lines using pseudo-elements. This technique is worth understanding deeply, because it can be applied to many other contexts where you need to draw decorative lines or shapes without adding extra HTML elements.
Why Pseudo-Elements?
CSS pseudo-elements (::before and ::after) generate virtual elements that can be styled and positioned independently of the document structure. They exist in the rendered DOM but are not in the HTML source, which means they can be used to add purely decorative content without cluttering the markup.
For drawing connecting lines in a chart, this is ideal. Each line segment we need to draw is a thin rectangle — either a vertical line (width: 2px, height: some value) or a horizontal line (width: some value, height: 2px). By making these pseudo-elements with position: absolute, we can place them at precise positions relative to their parent node using top, right, bottom, and left coordinates.
Each HTML element has exactly two pseudo-elements available (::before and ::after), which limits us to two line segments per element. This is a significant constraint that shaped the HTML structure of our chart. The nested list structure — where each level's nodes are wrapped in an ordered list — is specifically designed to give us enough pseudo-elements to draw all the required line segments. The wrapper list itself contributes two pseudo-elements, and each list item contributes two more.
Anatomy of a Connecting Line
Let us trace exactly how the connecting line between Level 1 and Level 2 is drawn, to build a clear mental model of the technique:
- Vertical stem below Level 1 node: .level-1::before — a 2px wide, 20px tall pseudo-element positioned at top: 100%, left: 50% of the Level 1 node. This creates the short vertical drop from the bottom of the CEO box.
- Horizontal bar connecting the two branches: .level-2-wrapper::before — a 2px tall, 50% wide pseudo-element positioned at top: -20px (which aligns it with the bottom of the vertical stem above), spanning from the center of the left column to the center of the right column.
- Vertical stem above each Level 2 node: .level-2-wrapper > li::before — a 2px wide, 20px tall pseudo-element positioned at bottom: 100%, left: 50% of each list item. This creates the short vertical drop from the horizontal bar down to each Level 2 node.
The result is a T-shaped connector: a single vertical stem from Level 1 splits into two branches via a horizontal bar, and each branch drops down to a Level 2 node. This same T-shape is repeated at every level transition in the chart, with minor adjustments to the measurements to account for the different widths and spacings of each level.
![]()
Figure 5: A detailed technical diagram of the pseudo-element line drawing technique, annotated with CSS property values. Each line segment is a separate pseudo-element positioned absolutely relative to its parent node. The T-connector between any two levels consists of: (1) a vertical stem below the parent node, (2) a horizontal bar above the child level, and (3) vertical stems above each child node. Together these four elements create the branching connector pattern characteristic of organizational charts. (Credit: George Martsoukos / Envato Tuts+)
A Comparison: Different Connector Styles at Each Level
It is worth noting that the connector style at Level 4 is deliberately different from the connector styles at Levels 2 and 3. While Levels 2 and 3 use the T-connector with a horizontal bar (appropriate for nodes arranged horizontally), Level 4 uses a vertical sidebar connector (appropriate for nodes arranged vertically). This variation demonstrates that the pseudo-element technique is flexible enough to handle different node arrangements within the same chart.
| Level | Node Arrangement | Connector Style | Pseudo-elements Used |
| Level 1 → 2 | Horizontal (2 nodes) | T-connector with horizontal bar | .level-1::before + .level-2-wrapper::before + li::before |
| Level 2 → 3 | Horizontal (2 nodes per group) | T-connector with horizontal bar | .level-2::before + .level-3-wrapper::before + li::before |
| Level 3 → 4 | Vertical (4 nodes per group) | Vertical sidebar + horizontal stubs | .level-4-wrapper::before + .level-4::before |
Table 1: Comparison of connector styles used at each level transition in the organizational chart. The connector style is chosen based on the arrangement of child nodes: horizontal arrangements use the T-connector pattern, while vertical arrangements use the sidebar connector pattern.
Making the Chart Responsive
Making an organizational chart responsive is significantly more challenging than making a simple linear layout responsive. The horizontal spread of a multi-level chart can easily exceed the available screen width on mobile devices, requiring a fundamental restructuring of the layout rather than simple adjustments.
The Mobile Layout Strategy
On mobile screens (below 700px), the chart switches from a horizontal tree layout to a vertical list layout. Instead of Level 2 nodes sitting side by side horizontally, they stack vertically. The connecting lines change from T-connectors (appropriate for horizontal siblings) to vertical sidebar lines (appropriate for vertical siblings, similar to the Level 4 desktop layout).
This is a significant change that affects both the visual structure and the CSS properties used. The responsive CSS must therefore hide certain pseudo-elements that are only relevant for the desktop layout, show certain pseudo-elements that are only relevant for the mobile layout, and change several layout properties.
CSS — Responsive Layout (max-width: 700px)
@media screen and (max-width: 700px) { .rectangle { padding: 20px 10px; /* reduced horizontal padding */ } /* Full-width nodes on mobile */ .level-1, .level-2 { width: 100%; } .level-1 { margin-bottom: 20px; } /* Hide desktop-only connectors */ .level-1::before, .level-2-wrapper > li::before { display: none; } /* Show mobile-only connectors */ .level-2-wrapper, .level-2-wrapper::after, .level-2::after { display: block; } /* Stack columns vertically on mobile */ .level-2-wrapper { width: 90%; margin-left: 10%; } /* Convert horizontal bar to vertical sidebar */ .level-2-wrapper::before { left: -20px; width: 2px; height: calc(100% + 40px); } /* Spacing between stacked nodes */ .level-2-wrapper > li:not(:first-child) { margin-top: 50px; } }
Key Responsive Techniques Explained
Several specific techniques in the responsive CSS are worth examining closely, as they illustrate broader responsive design principles.
The transformation of the .level-2-wrapper::before pseudo-element from a horizontal bar (width: 50%, height: 2px) to a vertical sidebar (width: 2px, height: calc(100% + 40px)) is achieved by simply overriding the relevant properties in the media query. This demonstrates the flexibility of the pseudo-element approach: the same pseudo-element can serve completely different visual purposes in different contexts by changing its dimensions.
The display: none technique for hiding desktop-only connectors is a clean solution that takes advantage of the fact that pseudo-elements respect display: none. Setting display: none on an element hides both its own ::before and ::after pseudo-elements along with the element itself when used on the list items. On the pseudo-elements directly, it simply removes them from the layout.
The use of .level-2-wrapper > li:not(:first-child) to add top margin only between stacked items (not before the first item) is an example of the adjacent sibling combinator pattern that prevents double-spacing at the top of the list.
![]()
Figure 6: The mobile responsive layout of the organizational chart at viewport widths below 700px. The horizontal tree structure collapses into a vertical list layout, with nodes stacking from top to bottom instead of left to right. The connecting lines are reconfigured from T-connectors to vertical sidebar connectors, consistent with the direction of the layout. This responsive behavior is achieved entirely through CSS media queries, without any JavaScript. (Credit: George Martsoukos / Envato Tuts+)
The Mobile-First Recommendation
The author of this tutorial notes that making an organizational chart responsive required reconstructing the markup multiple times before arriving at the final version. The key lesson learned from this process is to follow a mobile-first approach: design the mobile layout first, then add styles for wider screens as enhancements.
In a mobile-first implementation, the base CSS (without any media queries) would define the mobile layout, and the desktop layout would be specified inside a min-width media query. This approach tends to produce simpler, more maintainable code because the simpler layout (vertical, single-column) becomes the baseline, and the more complex layout (horizontal, multi-column) is added as an enhancement for larger screens.
For a complex component like an organizational chart, mobile-first design also forces you to think carefully about the information architecture before getting caught up in the visual complexity of the desktop layout. A chart that works well as a vertical list on mobile is much easier to adapt to a horizontal tree on desktop than vice versa.
Complete HTML Structure
Now that we have covered each piece individually, let us see the complete HTML structure of the organizational chart. This brings together all four levels in their nested hierarchy:
HTML — Complete Organizational Chart Structure
<div class="container"> <h1 class="level-1 rectangle">CEO</h1> <ol class="level-2-wrapper"> <!-- Left branch (CTO) --> <li> <h2 class="level-2 rectangle">CTO</h2> <ol class="level-3-wrapper"> <li> <h3 class="level-3 rectangle">Engineering</h3> <ol class="level-4-wrapper"> <li><h4 class="level-4 rectangle">Frontend Dev</h4></li> <li><h4 class="level-4 rectangle">Backend Dev</h4></li> <li><h4 class="level-4 rectangle">DevOps</h4></li> <li><h4 class="level-4 rectangle">QA Engineer</h4></li> </ol> </li> <li> <h3 class="level-3 rectangle">Design</h3> <ol class="level-4-wrapper"> <li><h4 class="level-4 rectangle">UX Designer</h4></li> <li><h4 class="level-4 rectangle">UI Designer</h4></li> <li><h4 class="level-4 rectangle">Brand Designer</h4></li> <li><h4 class="level-4 rectangle">Motion Designer</h4></li> </ol> </li> </ol> </li> <!-- Right branch (CFO) --> <li> <h2 class="level-2 rectangle">CFO</h2> <!-- ... same structure for right branch ... --> </li> </ol> </div>
The nested structure mirrors the hierarchy it represents: just as the CTO manages the Engineering and Design departments, the CTO list item in the HTML contains the Engineering and Design list items as children. This semantic alignment between the HTML structure and the organizational structure it represents is good for both maintainability and accessibility.
Using ordered lists (ol) for the chart structure is semantically appropriate — the items in each level have a meaningful order (they are not random), and the list structure communicates hierarchy to screen readers. Using heading elements (h1 through h4) for the node content ensures that the chart's hierarchy is also reflected in the document's heading structure.
Customization and Extensions
The chart built in this tutorial is a specific configuration — four levels, specific numbers of nodes per level, specific connecting line styles. Most real-world use cases will require modifications. This section explores the most common customizations and how to implement them.
Adding or Removing Nodes
Adding nodes to an existing level is straightforward: add a new list item within the appropriate ordered list. The CSS Grid layout will automatically accommodate the new node by adjusting the column widths. However, you may need to adjust the connecting line pseudo-elements if the number of columns changes significantly.
Adding an entirely new level requires more significant CSS work: you need to define a new wrapper class, new node styles, and new connecting line pseudo-elements. Follow the same patterns established for existing levels: a wrapper with position: relative and CSS Grid, a ::before pseudo-element on the wrapper for the horizontal bar, and ::before pseudo-elements on each list item for the vertical stems.
Changing Colors
Changing the color scheme is the easiest customization: simply update the four custom property values in the :root block. Because all color references throughout the stylesheet use these variables, changing the variables updates the entire chart at once. This makes it trivial to create multiple themes for different organizations or branding contexts.
Adding Images or Icons to Nodes
Real organizational charts often include profile photographs or role icons in each node. These can be added inside the heading elements. Because the rectangle class already handles the card-like container styling, you simply need to add an img element inside the appropriate heading and style it appropriately.
For example, to add a circular profile photo, you might use: a fixed-size img element with border-radius: 50% and object-fit: cover. The padding on the rectangle class provides space around the content, so the image will sit cleanly within the node.
When to Use a JavaScript Library Instead
The pure CSS approach is ideal for organizational charts with a static, known structure. If your chart needs to be generated from data, updated dynamically, allow user interaction (like expanding and collapsing branches), or handle an unknown number of levels and nodes, a JavaScript library will serve you better.
Libraries like Highcharts.js, D3.js, OrgChart.js, and vis.js all offer organizational chart functionality with dynamic data binding, interactive features, and extensive customization options. The tradeoff is the additional JavaScript payload and the API learning curve. For dynamic, data-driven charts, this tradeoff is almost always worthwhile. For static, design-controlled charts, the pure CSS approach remains compelling.
💡 Decision Framework: Use pure CSS for: static charts with known structure, performance-critical pages, environments where JavaScript is unavailable, and educational or portfolio contexts. Use JavaScript libraries for: data-driven charts, interactive charts, charts with unknown structure, and applications requiring frequent updates.
Accessibility Considerations
Building accessible organizational charts requires attention to both the semantic HTML structure and the experience for users who rely on assistive technologies like screen readers. A chart that looks great visually but is opaque to screen reader users is not truly complete.
Semantic HTML and Heading Hierarchy
The use of heading elements (h1 through h4) for the node content in our chart serves double duty: it provides semantic meaning to the document structure that screen readers can navigate, and it creates a natural heading hierarchy that reflects the organizational hierarchy. Users navigating by headings will encounter the chart content in a logical top-down order.
The use of ordered lists (ol) for the chart structure also benefits accessibility. Screen readers announce list elements, giving users contextual information about where they are in the hierarchy ("list, 4 items" when entering a level-4 wrapper, for example). This helps users understand the structure of the chart even when they cannot see its visual representation.
Alternative Text and ARIA
For more complex organizational charts where the visual structure communicates information that is not fully captured by the heading and list structure alone, ARIA attributes can supplement the semantic HTML. The role="tree" and role="treeitem" ARIA roles specifically describe hierarchical structures, and aria-label attributes can provide additional context.
For charts that are embedded in a page alongside other content, an introductory paragraph that describes the chart's purpose and top-level structure helps all users, including those using assistive technologies, understand what the chart represents before they encounter its details.
Keyboard Navigation
The pure HTML list structure of our chart supports keyboard navigation by default: Tab moves between focusable elements, and arrow keys navigate within lists in browsers that support list navigation. If the nodes in the chart contain interactive elements (buttons, links), these should be reachable and operable via keyboard without requiring mouse interaction.
For charts that need more sophisticated keyboard navigation — such as the ability to expand and collapse branches with keyboard input — JavaScript is required. The ARIA tree pattern provides guidance on implementing accessible keyboard navigation for hierarchical structures.
Conclusion: The Power of Pure CSS
Building an organizational chart in pure CSS is a genuinely challenging exercise that rewards careful thought about HTML structure, creative use of pseudo-elements, and systematic application of CSS Grid. The result a fully responsive, visually polished, JavaScript-free chart component — demonstrates that CSS is more powerful than many developers assume.
The specific techniques used in this tutorial pseudo-element line drawing, nested CSS Grid layouts, responsive redesign via media queries, CSS custom properties for theming are all broadly applicable skills that extend well beyond organizational charts. The pattern of using ::before and ::after pseudo-elements to draw decorative lines and shapes without extra HTML elements is particularly versatile and appears in countless CSS implementations of navigation patterns, timeline designs, and data visualizations.
The organizational chart is a reminder that the distinction between "design" and "development" is often artificial. The visual structure of the chart the T-connectors, the color-coded levels, the responsive collapse from horizontal to vertical is inseparable from the technical decisions about HTML structure and CSS implementation. Understanding both sides of this coin is what distinguishes a well-crafted web component from a merely functional one.
Whether you use this implementation directly, adapt it for a specific design requirement, or use it as inspiration for your own CSS chart experiments, the underlying principles will serve you well. The next time you encounter a hierarchical data structure that needs to be visualized on the web, consider what pure CSS can do before reaching for a JavaScript library.
FAQ About CSS Organizational Charts
1. What is a CSS organizational chart?
A CSS organizational chart is a visual diagram that shows hierarchical relationships between roles, departments, or entities using only HTML and CSS. It represents parent-child relationships in a tree structure without relying on JavaScript libraries.
2. Can you build an organizational chart without JavaScript?
Yes, it is possible to build an organizational chart using only HTML and CSS. By using CSS Grid, pseudo-elements like ::before and ::after, and proper positioning, developers can create connecting lines and structured layouts without JavaScript.
3. Why use pure CSS for an organizational chart?
Using pure CSS offers several advantages, including better performance, fewer dependencies, and simpler maintenance. Since there is no JavaScript involved, the page loads faster and the chart remains lightweight.
4. What CSS techniques are used to create organizational charts?
Several CSS techniques are commonly used, including:
-
CSS Grid for layout structure
-
Pseudo-elements (::before and ::after) for drawing connector lines
-
Absolute positioning for precise line placement
-
Media queries for responsive layouts
5. Are CSS organizational charts responsive?
Yes, CSS organizational charts can be made responsive using media queries. On smaller screens, the layout can change from a horizontal hierarchy to a vertical structure for better readability on mobile devices.
6. What are the common use cases for organizational charts?
Organizational charts are commonly used for:
-
Company hierarchy diagrams
-
Family trees and genealogy
-
Knowledge taxonomies
-
File system structures
-
Software architecture diagrams
7. What is the difference between a CSS chart and a JavaScript chart library?
A CSS chart is static and built directly with HTML and CSS, while JavaScript chart libraries like Chart.js or D3.js generate charts dynamically from data and allow interactive features such as animations or updates.
8. When should you use a JavaScript library instead of CSS?
You should use a JavaScript library when the chart needs dynamic data updates, interactive features, or when the number of nodes and levels is unknown or frequently changing.
9. What is the role of pseudo-elements in CSS charts?
Pseudo-elements such as ::before and ::after are used to create visual connector lines between nodes. They allow developers to draw lines and shapes without adding extra HTML elements.
10. Can CSS organizational charts be customized?
Yes, they can easily be customized. Developers can change colors using CSS variables, adjust spacing and layout with CSS Grid, and add images, icons, or additional levels to match the design requirements.

Leave a Reply