It turns out that, at least for the time being, I work on a design system for a living. I also happen to really like working on them, so here I am (on the weekend!) blogging about some web component-based design systems I've come across recently.
Advertisement
generic is a cool idea in that it's a set of (mostly) unstyled components you can pull in and style however you like. And maybe you've heard that web components are hard to style because they use shadow DOM: well good news, friend, because generic's components are mostly light DOM. Where generic does use shadow DOM, it still exposes shadow parts or provides custom properties to make the component styleable (see the generic-radio for an example with both).
Advertisement
What's great about leveraging light DOM so much is that, in addition to being easier to style, it's also more accessible and performant. The more content that goes into light DOM, the less work the custom element JS needs to do, the more content is available immediately, and less depends on JS executing at all. This jargony name for this architecture now is HTML Web Components (although generic predates this coinage by a couple of years), but the basic idea is just that you should leverage semantic HTML for as much of a web component's content and functionality as possible.
Another web component-based design system I've been looking at this week is tyler forge, by Tyler Technologies. It's not unstyled like generic. Its design is based on Material. It's also not shy about using shadow DOM. Different strokes, etc. What I like about it is its modularity. The way it does theming by exposing custom properties and shadow parts is a lot like what I want to do with USWDS. It's also got CSS-only components that are, um, CSS-only. I think the thing that jumped out at me was the mixed Sass/CSS custom props codebase, which is what USWDS is likely to be for a long time. I'm not sure I have anything smart to say about tyler forge, but I wanted to jot down some observations while they're fresh in my mind. extremely Marge Simpson voice I just think it's neat.
One last thing: just going to drop this prototype accordion component here. I've been wanting to make something like this for USWDS (which is why the tag name here is usa-accordion). It's just an accordion made out of details and summary elements, and it's all light DOM, so the basic functionality will always work. The only thing the custom element adds is the exclusive prop which, if set to true, will add a unique name to all of the details child nodes, triggering the browser-native exclusive accordion behavior. Since the unique ID is generated per-component, multiple accordions won't interfere with each other.
My favorite project that I've ever worked on is one from my early days at 18F. It's the one that really helped me get what the work was. I'm going to talk about everything I was lucky enough to work on on this project and some of the things I learned doing it. I'd love it if you took away some lessons about how great projects work, but I'd be lying if I said I didn't hope you'd read it and think about what a shame it is that the group who did this work no longer exists.
I won't name the partner for this project, and I'll leave out some details so as to not identify them. Given everything, I don't really want to call extra attention to 18F's partners, and honestly I don't chase down all of the things I would need to get permission to go into more detail. Besides, the work and methods I'll talk about here would've been employed on so many different projects with so many amazing agency partners.
The background
This agency had a ton of data, going back over a century, scattered across multiple databases, in multiple formats, with multiple schemas. They needed to consolidate these in some way in order to make those data usable. In addition to the data wrangling, they also needed a new frontend to give their users access to those data. Naturally, with all of those data on the backend, and a new frontend in the works, they also needed an API to get data back & forth.
In addition to the technical challenges, this agency served a huge variety of users, including research scientists, internal customers, and members of the public who used the data for recreational purposes. Whatever solutions the agency deployed would need to serve these vastly different users and use cases.
The team
The team from 18F included a backend dev, a strategist, a visual designer/user researcher, and yours truly as the frontend dev. The backend dev and strategist also shared project lead responsibilities. On the partner side, we worked regularly with the product owners as well as two engineers. Since this was one of my first projects at 18F, it was also one of my first experiences working closely with career feds.
One of the first things that made this project such a great experience was having such engaged and empowered product owners on the partner side. They participated in all of our standups and sprint ceremonies. If there was a blocker, they would hear about it immediately and help get unstuck. It also really helped build a sense of shared ownership of and responsibility for the success of the project. I'll talk a lot about secret sauce in this post, but I think if there's one thing that can make or break a consulting project, this is it: having an engaged and empowered product owner work as closely as possible with the team.
The work
Backend
We had a good sample of a few data sources we were going to have to pool together, but not enough to make big conclusions about a schema for the whole system. Basically we had enough to start prototyping with. Our backend dev whipped up a simple Flask app as a first draft REST API. However, given the complexity of the data and the wide variety of queries the API would have to support, we wanted to test out alternative alternatives that might be more flexible, and landed on GraphQL.
This was my first time working with GraphQL, and at the time I had what I called "podcast knowledge" of it, meaning I'd heard a lot about it on podcasts but didn't know much else. Since there were bigger questions about what we were going to do with all of the data–questions better-suited toward someone with real backend chops–I took point on adapting our Flask API to support GraphQL. It ended up being a pretty straightforward job of adding graphene to the Flask app and wiring it to the existing endpoints. Out of the box, this gave us the graphiQL in-browser interface to demonstrate the kinds of queries the API could support so we could get confident that it would be flexible enough to do what we needed, to do user research on the queries with technical users, and to continue to get buy-in from stakeholders.
Frontend
The earliest designs were clickable wireframes, and these were what we used for the first user research sessions. At this point, I don't remember what tool they were built in (probably would've been Sketch or Adobe XD), but they were fairly low-fidelity. For user testing purposes, they were extremely path-dependent and so were very brittle. These artifacts were hard to test with and hard to iterate on, so we made the decision pretty early on to switch to a live in-browser prototype. I'd say this was another of those secret sauce decisions that really helped the project on the path to success (paths don't typically involve sauce but I'm leaving the mixed metaphor.
Once we made that decision, we had one last Sketch-based comp to get something slightly higher fidelity to work from, but the rest of the design happened in the browser, where the designer & I paired on everything. Having a great design system as a starting point made this process so much easier. From this point on, all of the user research happened with a live prototype, talking to a real (prototype) API. After synthesizing each round of research, we could immediately iterate and fold updates right back into the prototype. The dream.
User research
I mentioned before that the product was going to need to support a huge variety of users with vastly different expectations for what data they would need and how they should be able to get it. Given that, it was important to get representatives from as many types of users as possible into our research interviews. Recreational users would need to be able to easily get hyperlocal data and see it in a way that would be understandable and meaningful to them. Expert users would need to be able to get all and only the data they need, ideally through UI-driven queries. In the event that the UI didn't do what a user needed, we could test queries in the graphiQL interface to see what they needed that we couldn't yet do. And finally, for those who needed it, we added in bulk download options that would let users get everything and crunch it as they saw fit in their own tooling.
Making something that worked for all of theses users required lots of interviews, and thuse required access to lots of users. This is one of those areas where having a product owner closely involved was critical. First, having someone at the partner agency who knew exactly what we needed was critical for recruiting the right research participants. And having someone with domain expertise in the interviews themselves helped us drill down into questions we hadn't necessarily anticipated exploring, and this expertise was also super helpful when it came time to synthesize the research.
Takeaways
At the end of a few sprints, we had a great prototype of a new interface to this massive store of data. And we knew it would work really well for a wide range of users, because we'd asked a lot of them. All in all, this was about 90 days of work, and it helped the agency set a course for the next several years of development on this project. For me, it showed how much a small cross-functional team could do
Find a product owner who can be a real part of your team, and get them as invlolved as you can.
Get real, working code in front of users as soon as you can.
Do as many interviews as you can with real users. Until you get something in front of users, you might have ideas about what can work, but you don't actually know anything.
As excited as I was to join 18F, I admit that before this project I didn't fully understand what was so great about 18F's methods. Once I saw these practices in action and the kinds of results they could bring about, not only did I get it, but honestly I wouldn't shut up about it. I preached this gospel with the zeal of the converted. Maybe most importantly, I spent the rest of my time at 18F trying to put these lessons into practice and doing what I could to help the org keep doing this critical work.
I'm grateful I got to work on this project, and a bunch more like it during my time at 18F. And while I'm sorry to end this post on a bummer note, revisiting this just drives home for me just what how much was lost when 18F was shuttered.
Also, if you want to learn more about these pracices so you can use them in your own work, the former 18F team rescued all the 18F Guides and added them to 18F.org.
Over the weekend, 18F was eliminated, and all of its staff placed on administrative leave in anticipation of imminent termination.
I left 18F in November, so I wasn't directly affected by this massacre. But I was at 18F for over 5 years and these were some of my closest colleagues and dear friends, so it's not that I wasn't affected at all. The secondhand trauma is real. The survivor's guilt is real. Honestly, the small twinge of jealousy at the clarity of purpose my former coworkers now have is real, too.
When I left 18F to join the USWDS team, I moved from one dream job to another. But the move was, in part, a strategic bet that I'd be in a safer place depending on the election results. After this weekend, there's a sense in which that bet paid off. But there aren't any safe jobs in the federal government, and there sure as hell aren't any dream jobs. One of Project 2025's architects, and now chief of the Office of Management and Budget expressed a plan to put federal workers in trauma. That effort has been a wild and terrible success.
Why did this happen?
(Even though I'm writing this on a personal device on my personal time, I still work for GSA and am still bound by its social media policy as well as the Hatch Act. As such, I'm going to limit this to only public information and refrain from expressing certain political opinions)
Recently, one of Airbnb's cofounders announced he was working with DOGE to "to improve the user experience within our government." What if I told you there was a group of user researchers, engineers, product managers, and procurement specialists within the federal government whose job was to partner with other federal agencies, learn directly from them and the people they serve what problems they face, and come up with solutions tested with real users? Well there isn't. Not anymore, anyway, since they were all just fired. I'm being glib, but my point is that 18F excelled at doing all of the things DOGE purports to want, including dramatically eliminating waste. See here and here and here for just a few of so, so many examples.
So why did they kill 18F? If you ask me in my totally personal non-official opinion, it wasn't despite 18F's success but because of it. But don't take it from me, take it from 18F's last Executive Director:
The administration got rid of 18F under the cover of night. People who own skyscrapers are afraid of 100 people who make websites.
Not because of the latest tech fad, but because we proved the government can be fixed, the government can be made better and the government can work for the people. - Lindsay Young, on BlueSky
I joined 18F almost 6 years ago, during the first Trump administration. It was important to me to ensure that people could have good experiences of government services. At the very least, I wanted to help keep the lights on until it was time to build again. With the American Rescue Plan, Technology Modernization Fund, and other drastic interventions, it was once again time to build, and dammit 18F built. 18F built or helped build: IRS Direct File, get.gov, Federal Audit Clearinghouse, American Climate Corps (RIP), and so, so much else. But that's the rub isn't it? At the end of the day, you either think government can and should make people's lives better, or you don't. If you think government should be in the business of helping the public, you'd keep 18F and throw a bunch of resources at them. If you didn't think that, well....
Now what?
If you want to know what's next for everyone from 18F, stay tuned. I mentioned a bet earlier. Here's another bet: if you piss off nearly 100 of the most capable, driven humans around, I bet you're going to end up regretting it.
What's next for me? I really don't know. For the time being, I still have a job. I don't for how long that will be true. I definitely didn't expect to be looking for a new job anytime soon, so while I'd prefer to be more strategic about my next role, I may not have that kind of luxury. We'll see, I guess. I would really like to believe that, if I'm able to hang on, I can still do some good. But the disgraceful treatment of 18F makes it all too clear that doing good is not an option for the time being.
In the more immediate future, I'd like to write more about my time at 18F, so if you want to hear more about that, for now the best way to follow that is on BlueSky. (I'm an RSS guy and I'll make a feed for this site, but I'm trying to post things instead of yak shaving. Update: Ok I made an RSS feed, so I feel better about that at least). Right now though, I'm going to mourn just a little bit longer and support my 18F comrades however I can.
This was my second week no longer being at 18F and serving full-time as eng. lead for USWDS. I had big plans to make some progress on some ADRs I need to write. I made just slightly less progress than intended (read: none at all).
Not work as such, but it's been fun noodling on this website again. I like the colors. I like the octopus. Upgrading to Eleventy 3.0 was a breeze (probably because I'm barely using it so there weren't any breaking changes that actually impacted me and switching to esmodules meant changing, like, three lines).
Things I read
CSS Parts are saving you from a nightmare by Michael Warren. This is a response to some criticism of CSS shadow parts on an episode of Shop Talk. The criticism boils down to Chris and Dave wanting a "I know what I'm doing" selector for styling custom element internals. Warren's post argues, in essence: even if you think you want that, you really don't—at least not if you're using someone else's components. CSS Parts are part of the API surface of a custom element (along with any attributes, slots, custom properties, or public methods the component exposes). If you're using someone else's code and don't want stuff to break all the time, you should stick to using the public API!
Hobbies
Last week of the current block in this technique cycle. Next week's a deload (boooooo). But I did hit my old strict press 1RM for an easy triple!
I have also been running and I'm in a good place with that. It's a nice thing to do a few times a week and feel like my heart and lungs are in good shape.
I've been coasting on language learning a bit this week.
I'm working on a High Profile project I won't go into beyond saying that the portion of it I'm working on is a big Typescript/React project. I've been working on improving test coverage, and wrapped up testing a giant chunk of the front-end portion of the knowledge graph that drives the project.
Unrelated to project work, I had a big, stressful choice to make I'm being purposefully vague about. I was a wreck about it for about 24 hours, but once I had made a decision about it, I was serene as could be.
Things I read
Tailwind marketing and misinformation engine - I'll confess to having a strong anti-Tailwind bias. That said, I did recently go through the Frontend Masters Tailwind course course. I enjoyed it and came away with a better sense of when and why I might use Tailwind (for prototypes—it's for prototypes). So I have softened a bit in my opposition to Tailwind, but reading through this piece really drove home what a bizarrely toxic presence Tailwind's leadership is in the webdev ecosystem. Read the post and look at the keynote slides in there and come up with your own conclusion I guess but after seeing that stuff, it's not a mystery why online conversations about Tailwind are so pitched.
Training
I've been training olympic lifts full-time for 3 weeks now (5x/week, up from 3x/week + CrossFit), and I feel like I'm really starting to see some big technical improvements, especially in the snatch.
My conditioning is noticeably declining, though. That's something I need to address, but in a way that doesn't detract too much from my main training goals.
I always knew that <C-o> would let you jump back to the previous cursor position, even if it was in another file. (And when I say always, I mean always. Like Meno always knew geometry.) While I was backtracking around with <C-o> today, it occurred to me that there must be a way to jump forwards again, and of course there is. It's <C-i>. The one right next to o on my keyboard.
Bonus tip
I didn't learn this today, but it's still a good tip: similarly to <C-o> and <C-i>, you can jump to the last-edited line with '., and the last-edited part of the last-edited line with `. These marks are built into vim. But that's not the tip. The tip is that if you use which-key, which-key ships with plugins to display the current contents of your marks and registers when you hit ' or ", respectively. Marks and registers were both things I always meant to use more frequently, and this has helped me get there.
Lol ok another bonus
Ok this i really did also learn today--mere moments ago, in fact. If you want to escape a backtick in a markdown inline code block, use double backticks with space around them, like so:
The basic skill of programming is not writing code, it's solving incredibly tedious puzzles over and over every day for years to produce a new app that everyone will hate being forced to use.
Are you sick and tired of fighting specificity in your CSS selectors? Have you ever wished you could tell the browser: "hey, my components' styles should take precedence over competing design system rules"? Well friend, do I have news for you. Cascade layers are here, and they basically let you do just that.
I knew these were a thing, but hadn't had occasion to really do anything with them yet. Then layers came up on the most recent episode of Shoptalk, and since I have a web project in progress right now (this. It's this website), I thought "ok let's do this."
The layers usage here is pretty simple, which oughtn't surprise, given that it's a simple site, but I think it still shows some of what the feature can do. All I'm doing is putting the third-party styles (in this case, normalize.css) in its own layer, and my styles in a layer on top. Even here you can see the utility: if I'm overriding something in the third party styles, I can stack (pun not intended) the deck in my styles' favor without having to arbitrarily increase the specificity. And if I wanted to add another 3rd-party library, like OpenProps or something, I could give that its own layer between the reset and my stuff.
@layer normalize, mh;
@import 'normalize.css' layer(normalize);
@layer mh {
/* ...all of my other CSS goes here */
}