Reading List
The most recent articles from a list of feeds I subscribe to.
Unbreaking.
Here’s to new work, and to a little more clarity amid the chaos.
I fight bots in my free time
Construction Lines
I recently stumbled across The Oatmeal’s series on Creativity. While all of it is spot on, the part on erasers hit especially hard.
“There is a lot of shame associated with backpedaling; things like quitting your job, getting a divorce, or simply starting over are considered shameful.
But forward isn’t always progress.
And backward isn’t always regress.
Sometimes going down the wrong path isn’t a mistake — it’s a construction line.”
It was exactly what I needed to hear. You see, only a few days prior, Font Awesome and I had parted ways — the end of a short, but transformative chapter. I’m proud of what we built together, and grateful for what I learned along the way. But it was time to move on.
Jobs are a lot like relationships. They often start with infatuation — and end with the realization that you’re simply not compatible, and that’s no-one’s fault. Letting go always stings, even when it’s the right call. There’s always grief: when you’re not ready to move on, you grieve the bond; when you are, you grieve your expectations. But every ending leaves behind clarity — about who you are, and what makes you happy.
The pursuit of happiness
Today is my 39th birthday — and this summer marks 20 years since I first dipped my toes into this industry. Naturally, I’ve been doing a lot of reflection.
As is typical for ADHDers, I have done a ton of different things, and built a diverse skillset as a result. But what made me happiest? The list of highs went a bit like this:
- Entrepreneurship: Co-founding a startup and driving it to become a household name (in Greece — this was 2008!)
- Consulting: Being a full-time consultant, speaker, and author, traveling the world and jumping from one exciting gig to another
- Academia:[1] Pushing the boundaries of Human-Computer Interaction at MIT and teaching MIT CS students to care about people.
All had three things in common: autonomy, breadth, and impact.
These three things have been the biggest predictors of happiness for me — far more than income [2] or work-life balance, which are the usual suspects.
I used to aspire to work-life balance in the same way I aspired to frequent exercise — because it was good for me, not because it gave me joy. Eventually I realized that what makes me happy isn’t working less, it’s loving what I do, and feeling it matters. Working less cannot transform how your work makes you feel; it can only dampen the effects. But dilution doesn’t turn misery into joy — at best it just makes it tolerable. Don’t get me wrong, poor WLB can absolutely make you miserable; when long hours are an externally imposed expectation, not an internal drive fueled by passion. As with many things in life, it’s enthusiastic consent that makes all the difference.
Then there’s breadth. Most jobs try to box you in: PM or engineer? Scientist or practitioner? UX Researcher or designer? DevRel or standards? And I’m like…
Aurora is my spirit animal 🫶🏼 Source
It’s silly that people are forced to choose, and present themselves as less than to seem more attractive to hiring managers. To use myself as an example:
- Web architecture: I have designed several web technologies that have shipped across all browsers. I’ve spent 4 years in the TAG, reviewing new web technologies across the web platform, and eventually leading the Web Platform Design Principles effort.
- Product/usability/HCI: Prior to working as Product Lead at Font Awesome, I’ve earned a PhD at MIT in Human-Computer Interaction with a minor in Entrepreneurship & Innovation. I published peer reviewed papers in top-tier HCI conferences, and co-created/taught a course on usability & web technologies that is now a permanent subject. I have run user research for scientific and industry projects. I have started several open source projects, some used by millions. In the more distant past, I co-founded a (then) well-known social startup in my home country, Greece and ran product, engineering, and design for three years (six if you count pre-incorporation).
- DevRel: I’ve given over 100 conference talks, published a bestselling book on CSS, and was the first devrel hire at W3C back in 2012. I have built dozens of apps and polyfills that stimulated developer interest in new Web features and drove browser adoption.
Should I present myself as a web architecture expert, a usability/product person, an HCI researcher, or a Developer Advocate?
What a pointless dilemma if I ever saw one!
Combining skills across different areas is a strength to be celebrated, not a weakness to be swept under the rug. The crossover between skills is where the magic happens. Off the top of my head:
- Understanding usability principles has made me a far better web standards designer.
- Web standards work is product design on hard mode. After the impossibly hard constraints and tradeoffs you deal with when designing APIs for the Web platform, regular product problems seem like a cakewalk (we have versions? And we can actually change things?? And we have reliable metrics?!? And the stakeholders all work at the same company?!? 🤯).
- They often feed into each other: DevRel work made me a better communicator in everything I do. Usability made me a better speaker and educator[3], so a better developer advocate too.
- Leading the Web Platform Design Principles convinced me that explicit design principles are immensely useful for all product work.
- Web standards taught me that contrary to popular belief, you do not need a benevolent dictator to ship. But then you do need a good process. Consensus does not magically grow on trees, building consensus is its own art.
Lastly, impact does not have to be about solving world hunger or curing cancer. Making people’s lives a little better is meaningful impact too. It all boils down to:
You can achieve the same total impact by improving the lives of a few people a lot, or the lives of many people a little. For example, my work on web standards has been some of the most fulfilling work I’ve ever done. Its Individual Impact is small, but the Reach is millions, since all front-end developers out there use the same web platform.
What’s next?
Since consulting and entrepreneurship have been my happiness peaks, I figured I’d try them again. Yes, both at once, because after all, we’ve already established that WLB is a foreign concept 🤣
My apprentice Dmitry and I have been in high gear building some exciting things, which I hope to be able to share soon, and I feel more exhilarated than I have in years. I had missed drawing my own lines.
In parallel, I’m taking on select consulting work, so if you need help with certain challenges, or to level up your team around web architecture, CSS, or usability, get in touch.
Don’t get me wrong, I’m not closing the door to full-time roles. I know there are roles out there that value passion and offer the kind of autonomy, breadth, and impact that would let me thrive. It’s the ROI of digging through rubble to find them that gives me pause — as a product person at heart, I/E tradeoffs are top of mind. But if you have such a unicorn, I’m all ears.
I also finally took a few small steps to make my pro bono work financially sustainable, a long overdue todo item. Both pages still need work, but you can now support my writing via ko-fi[4], and my open source work via GitHub Sponsors. I made separate pages for my two most popular projects, Prism (nearing 1.8 billion total npm installs! 🤯) and Color.js. This is as much about prioritization as it is about sustainability: money is an excellent signal about what truly matters to people.
I don’t have a polished “next” to announce yet.
But I’m exactly where I need to be.
Sometimes the clearest lines are the ones drawn after you erase.
Many things wrong with academia, but the intellectual freedom is unparalleled, and it makes up for a lot. ↩︎
See also Alan Watts’ “What if money was no object?” — a classic, but still relevant. ↩︎
Teaching is absolutely a form of UI design — a UI that exposes your knowledge to students — the users. There are many similarities between how good educators design their material and how good UI designers design interfaces. ↩︎
Thanks Dan Abramov for the wording inspiration (with permission). These things are so hard. ↩︎
Safari at WWDC '25: The Ghost of Christmas Past
At Apple's annual developer marketing conference, the Safari team announced a sizeable set of features that will be available in a few months. Substantially all of them are already shipped in leading-edge browsers. Here's the list, prefixed by the year that these features shipped to stable in Chromium:
- : WebGPU
- : SVG Favicons
- : HDR Images
- : CSS Anchor Positioning
- : CSS
text-wrap: pretty
- : CSS
progress()
function - : Scroll-driven Animations (finally!!!)
- : Trusted Types
- : URL Pattern API
- : WebAuthN Signal API
- : WritableStreams for the File System APIs
- : Scroll Margin for Intersection Observers
- : CSS Logical Overflow (
overflow-block
andoverflow-inline
) - : CSS
align-self
andjustify-self
in absolute positioning - : a subset of Explicit JavaScript Resource Management
- :
AudioEncoder
&AudioDecoder
for WebCodecs - :
RTCEncodedAudioFrame
&RTCEncodedVideoFrame
serialisation - :
RTCEncodedAudioFrame
&RTCEncodedVideoFrame
constructors - : PCM format support in MediaRecorder
- :
ImageCapture.grabFrame()
- : SVG
pointer-events="bounding-box"
- :
<link rel=dns-prefetch>
In many cases, these features were available to developers even earlier via the Origin Trials mechanism. WebGPU, e.g., ran trials for a year, allowing developers to try the in-development feature on live sites in Chrome and Edge as early as September 2021.
There are features that Apple appears to be leading on in this release, but it's not clear that they will become available in Safari before Chromium-based browsers launch them, given that the announcement is about a beta:
- Digital Credentials API: currently in Chromium OT.
- CSS
contrast-color()
margin-trim: block inline
extends a neat Safari-only feature in useful ways.- Apple's ALAC format for MediaRecorder.
- Audio Output Devices API. In progress in Chromium, but no launch scheduled.
The announced support for CSS image crossorigin()
and referrerpolicy()
modifiers has an unclear relationship to other browsers, judging by the wpt.fyi tests.
On balance, this is a lot of catch-up with sparse sprinklings of leadership. This makes sense, because Safari is in usually in last place when it comes to feature completeness:

And that is important because Apple's incredibly shoddy work impacts every single browser on iOS.
You might recall that Apple was required by the EC to enable browser engine choice for EU citizens under the Digital Markets Act. Cupertino, per usual, was extremely chill about it, threatening to end PWAs entirely and offering APIs that are inadequate or broken.
And those are just the technical obstacles that Apple has put up. The proposed contractual terms (pdf) are so obviously onerous that no browser vendor could ever accept them, and are transparently disallowed under the DMA's plain language. But respecting the plain language of the law isn't Apple's bag.
All of this is to say that Apple is not going to allow better browsers on iOS without a fight, and it remains dramatically behind the best engines in performance, security, and features. Meanwhile, we now know that Apple is likely skimming something like $19BN per year in pure profit from it's $20+BN/yr of revenue from its deal with Google. That's a 90+% profit rate, which is only reduced by the paltry amount it re-invests into WebKit and Safari.
So to recap: Apple's Developer Relations folks want you to be grateful to Cupertino for unlocking access to features that Apple has been the singular obstacle to.
And they want to you ignore the fact that for the past decade it has hobbled the web while skimming obscene profits from the ecosystem.
Don't fall for it. Ignore the gaslighting. Apple could 10x the size of the WebKit team without causing the CTO to break a sweat, and there are plenty of great browser engineers on the market today. Suppressing the web is a choice — Apple's choice — and not one that we need to feel gratitude toward.
Using `make` to compile C programs (for non-C-programmers)
I have never been a C programmer but every so often I need to compile a C/C++
program from source. This has been kind of a struggle for me: for a
long time, my approach was basically “install the dependencies, run make
, if
it doesn’t work, either try to find a binary someone has compiled or give up”.
“Hope someone else has compiled it” worked pretty well when I was running Linux but since I’ve been using a Mac for the last couple of years I’ve been running into more situations where I have to actually compile programs myself.
So let’s talk about what you might have to do to compile a C program! I’ll use a couple of examples of specific C programs I’ve compiled and talk about a few things that can go wrong. Here are three programs we’ll be talking about compiling:
step 1: install a C compiler
This is pretty simple: on an Ubuntu system if I don’t already have a C compiler I’ll install one with:
sudo apt-get install build-essential
This installs gcc
, g++
, and make
. The situation on a Mac is more
confusing but it’s something like “install xcode command line tools”.
step 2: install the program’s dependencies
Unlike some newer programming languages, C doesn’t have a dependency manager. So if a program has any dependencies, you need to hunt them down yourself. Thankfully because of this, C programmers usually keep their dependencies very minimal and often the dependencies will be available in whatever package manager you’re using.
There’s almost always a section explaining how to get the dependencies in the README, for example in paperjam’s README, it says:
To compile PaperJam, you need the headers for the libqpdf and libpaper libraries (usually available as libqpdf-dev and libpaper-dev packages).
You may need
a2x
(found in AsciiDoc) for building manual pages.
So on a Debian-based system you can install the dependencies like this.
sudo apt install -y libqpdf-dev libpaper-dev
If a README gives a name for a package (like libqpdf-dev
), I’d basically
always assume that they mean “in a Debian-based Linux distro”: if you’re on a
Mac brew install libqpdf-dev
will not work. I still have not 100% gotten
the hang of developing on a Mac yet so I don’t have many tips there yet. I
guess in this case it would be brew install qpdf
if you’re using Homebrew.
step 3: run ./configure
(if needed)
Some C programs come with a Makefile
and some instead come with a script called
./configure
. For example, if you download sqlite’s source code, it has a ./configure
script in
it instead of a Makefile.
My understanding of this ./configure
script is:
- You run it, it prints out a lot of somewhat inscrutable output, and then it
either generates a
Makefile
or fails because you’re missing some dependency - The
./configure
script is part of a system called autotools that I have never needed to learn anything about beyond “run it to generate aMakefile
”.
I think there might be some options you can pass to get the ./configure
script to produce a different Makefile
but I have never done that.
step 4: run make
The next step is to run make
to try to build a program. Some notes about
make
:
- Sometimes you can run
make -j8
to parallelize the build and make it go faster - It usually prints out a million compiler warnings when compiling the program. I always just ignore them. I didn’t write the software! The compiler warnings are not my problem.
compiler errors are often dependency problems
Here’s an error I got while compiling paperjam
on my Mac:
/opt/homebrew/Cellar/qpdf/12.0.0/include/qpdf/InputSource.hh:85:19: error: function definition does not declare parameters
85 | qpdf_offset_t last_offset{0};
| ^
Over the years I’ve learned it’s usually best not to overthink problems like
this: if it’s talking about qpdf
, there’s a good change it just means that
I’ve done something wrong with how I’m including the qpdf
dependency.
Now let’s talk about some ways to get the qpdf
dependency included in the right way.
the world’s shortest introduction to the compiler and linker
Before we talk about how to fix dependency problems: building C programs is split into 2 steps:
- Compiling the code into object files (with
gcc
orclang
) - Linking those object files into a final binary (with
ld
)
It’s important to know this when building a C program because sometimes you need to pass the right flags to the compiler and linker to tell them where to find the dependencies for the program you’re compiling.
make
uses environment variables to configure the compiler and linker
If I run make
on my Mac to install paperjam
, I get this error:
c++ -o paperjam paperjam.o pdf-tools.o parse.o cmds.o pdf.o -lqpdf -lpaper
ld: library 'qpdf' not found
This is not because qpdf
is not installed on my system (it actually is!). But
the compiler and linker don’t know how to find the qpdf
library. To fix this, we need to:
- pass
"-I/opt/homebrew/include"
to the compiler (to tell it where to find the header files) - pass
"-L/opt/homebrew/lib -liconv"
to the linker (to tell it where to find library files and to link iniconv
)
And we can get make
to pass those extra parameters to the compiler and linker using environment variables!
To see how this works: inside paperjam
’s Makefile you can see a bunch of environment variables, like LDLIBS
here:
paperjam: $(OBJS)
$(LD) -o $@ $^ $(LDLIBS)
Everything you put into the LDLIBS
environment variable gets passed to the
linker (ld
) as a command line argument.
secret environment variable: CPPFLAGS
Makefiles
sometimes define their own environment variables that they pass to
the compiler/linker, but make
also has a bunch of “implicit” environment
variables which it will automatically pass to the C compiler and linker. There’s a full list of implicit environment variables here,
but one of them is CPPFLAGS
, which gets automatically passed to the C compiler.
(technically it would be more normal to use CXXFLAGS
for this, but this
particular Makefile
hardcodes CXXFLAGS
so setting CPPFLAGS
was the only
way I could find to set the compiler flags without editing the Makefile
)
two ways to pass environment variables to make
I learned thanks to @zwol that there are actually two ways to pass environment variables to make
:
CXXFLAGS=xyz make
(the usual way)make CXXFLAGS=xyz
The difference between them is that make CXXFLAGS=xyz
will override the
value of CXXFLAGS
set in the Makefile
but CXXFLAGS=xyz make
won’t.
I’m not sure which way is the norm but I’m going to use the first way in this post.
how to use CPPFLAGS
and LDLIBS
to fix this compiler error
Now that we’ve talked about how CPPFLAGS
and LDLIBS
get passed to the
compiler and linker, here’s the final incantation that I used to get the
program to build successfully!
CPPFLAGS="-I/opt/homebrew/include" LDLIBS="-L/opt/homebrew/lib -liconv" make paperjam
This passes -I/opt/homebrew/include
to the compiler and -L/opt/homebrew/lib -liconv
to the linker.
Also I don’t want to pretend that I “magically” knew that those were the right arguments to pass, figuring them out involved a bunch of confused Googling that I skipped over in this post. I will say that:
- the
-I
compiler flag tells the compiler which directory to find header files in, like/opt/homebrew/include/qpdf/QPDF.hh
- the
-L
linker flag tells the linker which directory to find libraries in, like/opt/homebrew/lib/libqpdf.a
- the
-l
linker flag tells the linker which libraries to link in, like-liconv
means “link in theiconv
library”, or-lm
means “linkmath
”
tip: how to just build 1 specific file: make $FILENAME
Yesterday I discovered this cool tool called
qf which you can use to quickly
open files from the output of ripgrep
.
qf
is in a big directory of various tools, but I only wanted to compile qf
.
So I just compiled qf
, like this:
make qf
Basically if you know (or can guess) the output filename of the file you’re
trying to build, you can tell make
to just build that file by running make $FILENAME
tip: you don’t need a Makefile
I sometimes write 5-line C programs with no dependencies, and I just learned
that if I have a file called blah.c
, I can just compile it like this without creating a Makefile
:
make blah
It gets automaticaly expanded to cc -o blah blah.c
, which saves a bit of
typing. I have no idea if I’m going to remember this (I might just keep typing
gcc -o blah blah.c
anyway) but it seems like a fun trick.
tip: look at how other packaging systems built the same C program
If you’re having trouble building a C program, maybe other people had problems building it too! Every Linux distribution has build files for every package that they build, so even if you can’t install packages from that distribution directly, maybe you can get tips from that Linux distro for how to build the package. Realizing this (thanks to my friend Dave) was a huge ah-ha moment for me.
For example, this line from the nix package for paperjam
says:
env.NIX_LDFLAGS = lib.optionalString stdenv.hostPlatform.isDarwin "-liconv";
This is basically saying “pass the linker flag -liconv
to build this on a
Mac”, so that’s a clue we could use to build it.
That same file also says env.NIX_CFLAGS_COMPILE = "-DPOINTERHOLDER_TRANSITION=1";
. I’m not sure what this means, but when I try
to build the paperjam
package I do get an error about something called a
PointerHolder
, so I guess that’s somehow related to the “PointerHolder
transition”.
step 5: installing the binary
Once you’ve managed to compile the program, probably you want to install it somewhere!
Some Makefile
s have an install
target that let you install the tool on your
system with make install
. I’m always a bit scared of this (where is it going
to put the files? what if I want to uninstall them later?), so if I’m compiling
a pretty simple program I’ll often just manually copy the binary to install it
instead, like this:
cp qf ~/bin
step 6: maybe make your own package!
Once I figured out how to do all of this, I realized that I could use my new
make
knowledge to contribute a paperjam
package to Homebrew! Then I could
just brew install paperjam
on future systems.
The good thing is that even if the details of how all of the different packaging systems, they fundamentally all use C compilers and linkers.
it can be useful to understand a little about C even if you’re not a C programmer
I think all of this is an interesting example of how it can useful to understand some basics of how C programs work (like “they have header files”) even if you’re never planning to write a nontrivial C program if your life.
It feels good to have some ability to compile C/C++ programs myself, even
though I’m still not totally confident about all of the compiler and linker
flags and I still plan to never learn anything about how autotools works other
than “you run ./configure
to generate the Makefile
”.
Two things I left out of this post:
LD_LIBRARY_PATH / DYLD_LIBRARY_PATH
(which you use to tell the dynamic linker at runtime where to find dynamically linked files) because I can’t remember the last time I ran into anLD_LIBRARY_PATH
issue and couldn’t find an example.pkg-config
, which I think is important but I don’t understand yet