--- title: "Accessible design system" layout: "study" slug: "honeycomb" summary: "Building a native-first, accessible design system that could actually be adopted across six very different education products." draft: false hero: title: "Accessible design system" deck: "Building a **native-first, accessible design system** to support six different products, tech stacks, and development timelines." html: '
' list: more: "My company needed a design system to unify six very different products as one new edtech platform. We did that while managing very different frontends and skill levels, urgent accessibility needs, and no single framework that everyone could share." color: yellow facts: - key: Role color: blue - key: tools color: red - key: industry color: green study: facts: - label: "Role" value: "Principal designer, frontend architect" icon: "hat" class: "min" - label: "Timeline" value: "Ongoing production system" icon: "calendar" - label: "Tools" value: "HTML, CSS, JS, Typescript, Figma" icon: "pen" - label: "Industry" value: "K-12 education SaaS" icon: "briefcase" - label: "Scope" value: "6 products, many stacks" icon: "users" toc: - id: "intro" label: "Introduction" - id: "overview" label: "Project overview" - id: "constraints" label: "Problems" - id: highlights label: "Highlights" num: "04." children: - id: "native" label: "Browser-native" - id: "layout" label: "Unified layout" - id: "updates" label: "Stable updates" - id: "docs" label: "Documentation" - id: "impact" label: "Impact" - id: "reflection" label: "Reflection" footnotes: - id: "fn1" symbol: "†" note: "The DOJ's Title II web and mobile accessibility rule set WCAG 2.1 Level AA as the technical standard for state and local government web content and mobile apps. The original 2026 and 2027 compliance dates were later extended in 2026, but the rule's substance and procurement pressure remained important for education vendors." - id: "fn2" symbol: "‡" note: "I know that Tailwind generates a lot of emotion. I've been writing CSS by hand for 25 years (and love it) and I'm not ashamed to say that I enjoy Tailwind, though I don't use it all the time. This site for instance is all hand-written, and I've created my own \"mini Tailwinds\" at times too. It made sense for Honeycomb as it significantly speeds up the time to write CSS/Sass, files are much shorter using `@apply` on single lines, other teams were using it or wanted to, and the config file defining our theme/primitives was really helpful for us." - id: "fn3" symbol: "§" note: I fully appreciate how ridiculous it sounds to roll our own icon font! I have a lot of experience with them, though, and my manager was incredibly supportive in letting me really enjoy this project too. I think the reasoning stands on its own, but I also do just get a lot of enjoyment making them. --- {{< intro id="intro" title="A design system is only valuable if teams can actually use it" >}} Honeycomb was a large, foundational design-system effort: a **unified design language and implemented component library for _six acquired products_** that all looked, felt, and behaved differently. We designed and built 40+ components, plus the supporting patterns, layout scaffolds, utilities, documentation, and [connected](https://github.com/figma/code-connect) Figma components needed to make those products feel like they belonged to the same company. But this case study is not primarily about the mechanics of building a design system. Many systems define tokens, document components, and create a composable UI. The more interesting problem was **how we designed Honeycomb from the start _to be used_ inside our particular set of constraints**: different products, different codebases, different teams, different levels of frontend skill, and very different modernization timelines. That shaped both the earliest decisions and the later priorities: browser-native web technologies, a small JavaScript footprint, CSS variables for theming, progressive adoption, and [unusually thorough documentation](https://honeycomb.style). The system had to meet teams where they were, so the central design and engineering constraint became: make the right thing, but make it easy to adopt before the organization was technically uniform enough to deserve a perfect system. {{< /intro >}} {{< overview id="overview" title="Project overview" >}} Honeycomb was built to support six education products with different histories, product roadmaps, engineering teams, and frontend maturity. We had products that ran the gamut from "we manually copy `jquery.min.js` and cannot install npm" to "everything is React." The system also existed in two connected places: a Figma component library for design work, and an implemented HTML/CSS package documented through a hand-built site. {{< figure src="img/overview@2x.png" alt="Button component sample in Figma and documentation" caption="Button component variants in Figma (_left_) and a documented example (_right_)." >}} The package was intentionally native-first. Most components shipped as documented **HTML plus compiled CSS, with minimal JavaScript reserved for interactions that could not be handled reliably** with browser behavior alone. That meant product teams could drop in the CSS, copy the documented markup into whatever view file or component system they were using, and get a large amount of visual consistency and accessibility handling without committing to React, installing Tailwind, or rebuilding their app shell. This was also a design strategy. Our products needed to feel like one company without erasing their existing workflows overnight. The system provided shared foundations: header, left navigation, layout scaffolding, form controls, tables, buttons, alerts, theming, icons, and more complex components like datepickers and drawers. From there, each product could adopt progressively. {{< /overview >}} {{< problems id="constraints" title="Adoption constraints" >}} The first major decision was accepting that adoption mattered more than technical purity. While many were pushing React aggressively, our reality was that we could not pick a fashionable component model and tell every product team to catch up. {{< problem title="Six products had six different technical realities" >}} The system had to support product teams with very different stacks and skill levels. Some could add one more npm package easily. Some were entirely React-based and would eventually wrap Honeycomb in React components. Others needed a more basic path: copy a CSS file, include a font folder, and use documented HTML in server-rendered views. That ruled out a React-only system as the first deliverable. It also ruled out a system that assumed every team wanted to learn Tailwind, Sass, or a new build pipeline. The adoption path had to be boring in the best possible way 😏. {{< /problem >}} {{< problem title="Adoption had to be progressive with no massive rewrites" >}} There was no appetite for a synchronized redesign across every product. Each app had its own customer expectations and roadmap pressure, so **Honeycomb had to support progressive adoption**: a new header now, tables and form controls there, updated buttons and modals in the next feature area, then deeper layout changes when a team was ready. Components therefore needed stable class names that were prefixed to avoid any collisions, predictable HTML that would ideally never change, and compatibility with existing application markup. Our goal was that if a team adopted a component once, **future Honeycomb updates should only change presentation through CSS** and never require an underlying HTML update. {{< /problem >}} {{< problem title="The system had to cover patterns, not just components" >}} The most valuable work was often not a button or alert, it was the repeatable and difficult stuff product teams kept rebuilding inconsistently: app shells, responsive layouts, table controls, drawer-based filters, form sections, empty states, and primary/secondary navigation. By documenting those patterns, the design team handled decisions that would otherwise be made inconsistently across teams. Honeycomb became a shared product language, not just a visual toolkit. **Our design team became a shared product support team as well.** {{< /problem >}} {{< problem title="Federal accessibility requirements created real urgency" >}} Accessibility was not just a quality goal or a design value—it had become a business requirement. Most SchoolStatus customers are public school districts, and the [DOJ's Title II web and mobile accessibility rule](https://www.ada.gov/resources/2024-03-08-web-rule/) created a concrete expectation that public entities would bring web content and mobile apps into [WCAG 2.1 Level AA](https://www.w3.org/TR/WCAG21/) conformance. For an edtech platform this meant we could lose some of our largest districts (and theoretically almost all of our customers) if we didn't meet these requirements. It also meant that the the product teams could not just treat accessibility as something to "handle whenever we feel like it," and we could not rely on every team independently interpreting the WCAG, writing accessible markup, handling keyboard behavior, and testing every interaction with a screen reader. **The company needed accessibility that product teams could pick up off the shelf** as part of normal feature work. Honeycomb had to absorb as much of that responsibility as possible and we took it very seriously. * Everyone on the team got accessibility training. * Documentation included implementation rules and accessible attributes in every component, and distilled down requirements to plain English. * Our team tested everything with screen readers and ran through various disability simulators. * We also leveraged browser-native elements that brought accessibility in for free as much as possible. {{< /problem >}} {{< /problems >}}
{{< section id="native" title="Browser-native by default" >}} There are a bunch of projects wins and lessons that I'd love to highlight here, but I'm going to focus on four key ones that cover all the bases. The first and most important implementation decision we got right was that **Honeycomb would not belong to any single product stack** or require any kind of upgrade. We decided to be as out-of-the-way as possible and belong primarily to the browser. Despite the extended discussion, this pretty quickly ruled out a React-first design system early on. Some products would eventually wrap Honeycomb in React components (which was great!) but React could not be the system's lowest common denominator. **If the baseline required a modern JavaScript framework, two products would be locked out for too long** and the design system would fail at the thing it most needed to do: create consistency across the company before every app was modernized. So Honeycomb's first-class output was HTML and CSS. Each product team could import a compiled CSS file, copy documented markup, and start using components without changing their build process. That made adoption possible for older apps while still leaving a better path for teams with more modern tooling. ### Sass on top of Tailwind Under the hood, **we authored Honeycomb as Sass-based components on top of Tailwind**. Tailwind helped us write and maintain the system faster, and the decision was also practical: two products already used Tailwind, so they could compile Honeycomb alongside their own Tailwind configuration and benefit from the same tree-shaking and build process. Other products were encouraged to move in that direction (and one did), but they were not blocked if they could not get there immediately. {{< figure src="img/browser-tw@2x.png" alt="Sass code sample of the alert Honeycomb component" class="rounded" caption="We took advantage of Tailwind to better leverage our primitives and help write more readable Sass." >}} JavaScript followed the same model. We exported small vanilla JS modules, and later added a few framework-agnostic web components for interactions that needed more behavior. Teams could include those individually or as a bundle, but static components did not require JavaScript just to look and behave correctly. This included interactive components like dropdowns too! ### Native elements whenever possible We were also strict about using real browser controls wherever possible. A checkbox was an ``, not a div managed by a JavaScript checkbox library. A button was a `