652 stories
·
2 followers

Lessons Learned Shipping 500 Units of My First Hardware Product

1 Comment

Building in consumer hardware as a software engineer

1 year ago (Jan 2025) I quit my job as a software engineer to launch my first hardware product, Brighter, the world’s brightest lamp. In March, after $400k in sales through our crowdfunding campaign, I had to figure out how to manufacture 500 units for our first batch. I had no prior experience in hardware; I was counting on being able to pick it up quickly with the help of a couple of mechanical/electrical/firmware engineers.

The problems began immediately. I sent our prototype to a testing lab to verify the brightness and various colorimetry metrics. The tagline of Brighter was it’s 50,000 lumens — 25x brighter than a normal lamp. Instead, despite our planning & calculations, it tested at 39,000 lumens causing me to panic (just a little).

So with all hands on deck, in a couple of weeks we increased the power by 20%, redesigned the electronics to handle more LEDs, increased the size of the heatsink to dissipate the extra power, and improved the transmission of light through the diffuser.

alt

This time, we overshot to 60,000 lumens but I’m not complaining.

Confident in our new design I gave the go ahead to our main contract manufacturer in China to start production of mechanical parts. The heatsink had the longest lead time as it required a massive two ton die casting mold machined over the course of weeks. I planned my first trip to China for when the process would finish.

alt

Simultaneously in April, Trump announced “Liberation Day” tariffs, taking the tariff rate for the lamp to 50%, promptly climbing to 100% then 150% with the ensuing trade war. That was the worst period of my life; I would go to bed literally shaking with stress. In my opinion, Not Cool!

alt

I was advised to press forward with manufacturing because 150% is bonkers and will have to go down. So 2 months later in Zhongshan, China, I’m staring at a heatsink that looks completely fucked. Due to a miscommunication with the factory, the injection pins were moved inside the heatsink fins, causing the cylindrical extrusions below. I was just glad at least the factory existed.

alt
alt

I returned in August to test the full assembly with the now correct heatsink. At my electronics factory as soon as we connect all the wiring, we notice the controls are completely unresponsive. By Murphy’s Law (anything that can go wrong will go wrong), I had expected something like this to happen, so I made sure to visit the factory at 10am China Standard time, allowing me to coordinate with my electrical engineer at 9pm ET and my firmware engineer at 7:30am IST. We’re measuring voltages across every part of the lamp and none of it makes sense. I postpone my next supplier visit a couple days so I can get this sorted out. At the end of the day, we finally notice the labels on two PCB pins were swapped.

With a functional fully assembled lamp, we OK mass production of the electronics.

Our first full pieces from the production line come out mid October. I airship them to San Francisco, and hand deliver to our first customers. The rest are scheduled for container loading end of October.

alt
Wesley, CEO of Aragon.ai

Early customers give some good reviews:

alt
alt

People like the light! A big SF startup orders a lot more. However, there is one issue I hear multiple times: the knobs are scraping and feel horrible. With days until the 500 units are loaded into the container, I frantically call with the engineering team and factory. Obviously this shouldn’t be happening, we designed a gap between the knobs and the wall to spin freely. After rounds of back and forth and measurements, we figure out in the design for manufacturing (DFM) process, the drawings the CNC sub-supplier received did not have the label for spacing between the knobs, resulting in a 0.5mm larger distance than intended. Especially combined with the white powder coating which was thicker than the black finish, this caused some knobs to scrape.

alt
old knobs (too big)
alt
fixed knobs

Miraculously, within the remaining days before shipment, the factory remakes & powder coats 1000 new knobs that are 1mm smaller in diameter.

The factory sends me photos of the container being loaded. I have 3 weeks until the lamps arrive in the US — I enjoy the time without last minute engineering problems, albeit knowing inevitably problems will appear again when customers start getting their lamps.

alt
alt

The lamps are processed by our warehouse Monday, Dec 12th, and shipped out directly to customers via UPS. Starting Wednesday, around ~100 lamps are getting delivered every day. I wake up to 25 customer support emails and by the time I’m done answering them, I get 25 more. The primary issue people have is the bottom wires are too short compared to the tubes.

alt

It was at this point I truly began to appreciate Murphy’s law. In my case, anything not precisely specified and tested would without fail go wrong and bite me in the ass. Although we had specified the total length of the cable, we didn’t define the length of cable protruding from the base. As such, some assembly workers in the factory put far too much wire in the base of the lamp, not leaving enough for it to be assembled. Luckily customers were able to fix this by unscrewing the base, but far from an ideal experience.

There were other instances of quality control where I laughed at the absurdity: the lamp comes with a sheet of glass that goes over the LEDs, and a screwdriver & screws to attach it. For one customer, the screwdriver completely broke. (First time in my life I’ve seen a broken screwdriver…) For others, it came dull. The screwdriver sub supplier also shipped us two different types of screws, some of which were perfect, and others which were countersunk and consequently too short to be actually screwed in.

alt
alt
alt

Lessons Learned

1. Plan way, way more.

Coming from software, the most planning you’re exposed to is linear tickets, sprints, and setting OKRs. If you missed a deadline, it’s often because you re-prioritized, so no harm done.

In hardware, the development lifecycle of a product is many months. If you mess up tooling, or mass produce a part incorrectly, or just sub-optimally plan, you set back the timeline appreciably and there’s nothing you can do but curse yourself. I found myself reaching for more “old school” planning tools like Gantt charts, and also building my own tools. Make sure you have every step of the process accounted for. Assume you’ll go through many iterations of the same part; double your timelines.

In software, budgeting is fairly lax, especially in the VC funded startup space where all you need to know is your runway (mainly calculated from your employee salaries and cloud costs).

With [profitable] hardware businesses, your margin for error is much lower. Literally, your gross margin is lower! If you sell out because you miss a shipment or don’t forecast demand correctly, you lose revenue. If you mis-time your inventory buying, your bank account can easily go negative. Accounting is a must, and the more detailed the better. Spreadsheets are your best friend. The funding model is also much different: instead of relying heavily on equity, most growth is debt-financed. You have real liabilities!

2. Overcommunicate. Overspecify. Follow Up.

Anything that can go wrong will go wrong. Anything you don’t specify will fail to meet the implicit specification. Any project or component not actively pushed will stall. At previous (software) companies I’ve worked at, if someone followed up on a task, I took it to mean the task was off track and somebody was to blame. With a hardware product, there are a million balls in the air and you need to keep track of all of them. Though somewhat annoying, constant checkins simply math-out to be necessary. The cost of failure or delays is too high. Nowadays as a container gets closer to shipment date, I have daily calls with my factories. I found myself agreeing with a lot of Ben Kuhn’s blog post on running major projects (his blog post on lighting was also a major inspiration for the product).

3. Test everything, often, on many units

When I worked at Meta, every PR had to be accompanied with a test plan. I took that philosophy to Brighter, trying to rigorously test the outcomes we were aiming for (thermals, lumens, power, etc…), but I still encountered surprising failures. In software if you have coverage for a code path, you can feel pretty confident about it. Unfortunately hardware is almost the opposite of repeatable. Blink and you’ll get a different measurement. I’m not an expert, but at this point I’ve accepted the only way to get a semblance of confidence for my metrics is testing on multiple units in different environments.

4. Geopolitics matter

As someone who generally stays out of politics, I didn’t know much about the incoming administration’s stance towards tariffs, though I don’t think anyone could have predicted such drastic hikes. Regardless, it’s something you should be acutely aware of; take it into consideration when deciding what country to manufacture in, make sure it’s in your financial models with room to spare, etc…

5. Visit your suppliers early

I wish I had visited my suppliers much earlier, back when we were still prototyping with them. Price shouldn’t be an issue — a trip to China is going to be trivially cheap compared to buying inventory, even more so compared to messing up a manufacturing run due to miscommunication. Most suppliers don’t get international visitors often, especially Americans. Appearing in person conveys seriousness, and I found it greatly improved communication basically immediately after my first visit. Plus China is very different from the US and it’s cool to see!

What Did I Do Right?

To me, this process has felt like an exercise in making mistakes and learning painful lessons. However, I think I did do a couple of key things right:

1. Validated the market

The first thing I did before starting manufacturing—and even before the crowdfunding campaign—was setting up a simple website where people could pay $10 to get a steep discount off the MSRP. Before I committed time and money, I needed to know this would be self-sustaining from the get go. It turns out that people were happy to give their email and put down a deposit, even when the only product photos I had were from a render artist on fiverr!

2. Charged a sustainable amount

From talking to other hardware founders, these kinds of mistakes happen to everyone; hardware is hard as they say. It’s important to have a healthy enough business model to stomach these mistakes and still be able to grow.

Coolest Cooler had an incredibly successful crowdfunding campaign, partly because they packed a lot of features into a very attractively priced product. Unfortunately, it was too attractively priced, and partway through manufacturing they realized they didn’t have enough money to actually deliver all the units, leading to a slow and painful bankruptcy.

3. Prioritized customer support

When the first 500 units were being delivered, I knew there were bound to be issues. For that first week, I was literally chronically on my gmail. I would try to respond to every customer support issue within 1-2 minutes if possible (it was not conducive to my sleep that many of our customers were in the EU).

Some customers still had some issues with the control tube knobs & firmware. I acknowledged that they were subpar and decided to re-make the full batch of control tubes properly (with the correct knob spacing), as well as updated firmware & other improvements, and ship them to customers free of charge.

Overall, it’s been a very different but incredibly rewarding experience compared to working as a software engineer. It’s so cool to see something I built in my friends houses, and equally cool when people leave completely unprompted reviews:

alt
alt
alt

Adblock test (Why?)

Read the whole story
GaryBIshop
3 hours ago
reply
He's only done software but believes picking up hardware skills will be easy. The EE's at SUN assumed mechanical engineering was easy; they built some terrible cooling systems.
Share this story
Delete

PyBites: The missing 66% of your skillset

1 Comment

Bob and I have spent many years as Python devs, and 6 years coaching with Pybites and we can safely say that being a Senior Developer is only about 1/3 Python knowledge.

The other 60% is the ecosystem. It’s the tooling. It’s all of the tech around Python that makes you stand out from the rest.

This is the biggest blind spot keeping developers stuck in Tutorial Hell. You spend hours memorising obscure library features, but you crumble when asked to configure a CI/CD pipeline. (That’s not just made up by the way – many of you in dev roles will have seen this with colleagues at some point or another!)

These are the elements of the Python ecosystem you should absolutely be building experience with if you want to move from being a scripter to an engineer:

  • Dependency Management: Stop using pip freeze. Look at uv.
  • Git: Not just add/commit. Learn branching strategies and how to fix a merge conflict without panicking.
  • Testing: print() is not a test. Learn pytest and how to write good tests.
  • Quality Control: Set up Linters (Ruff) so you stop arguing about formatting, and ty for type checking.
  • Automation: Learn GitHub Actions (CI/CD). Make the robots run your tests for you.
  • Deployment: How does your code get to a server? Learn basic Docker and Cloud.
  • The CLI: Stop clicking buttons and get comfortable in the terminal. Learn Makefiles and create a make install or make test command to save your sanity.

It looks like a lot. It is a lot. But this is the difference between a hobbyist and a professional.

Does this make you feel overwhelmed? Or does it give you a roadmap of what to do this year?

I’m curious! Feel free to hit me up in the Community with your thoughts.

And yes, these are all things we coach people on in PDM. Use the link below to have a chat.

Julian

This note was originally sent to our email list. Join here: https://pybit.es/newsletter

Read the whole story
GaryBIshop
8 days ago
reply
Good advice.
Share this story
Delete

Saturday Morning Breakfast Cereal - Unified

2 Comments and 4 Shares


Click here to go see the bonus panel!

Hovertext:
10 points to anyone who tries this argument in real life.


Today's News:
Read the whole story
GaryBIshop
14 days ago
reply
Love this!
Share this story
Delete
1 public comment
jlvanderzwan
13 days ago
reply
The fun bit is that a large number of big names in biology these days are physicists who jumped ship because they realized physics was hitting a wall, and biologists are still naive about how to process big data.

The Confabulations of Oliver Sacks

1 Comment

I loved literature before I loved medicine, and as a medical student, I often found that my textbooks left me cold, their medical jargon somehow missing the point of profound diseases able to rewrite a person’s life and identity. I was born, I decided, a century too late: I found the stories I craved, not in contemporary textbooks, but in outdated case reports, 18th- and 19-century descriptions of how the diseases I was studying might shape the life of a single patient.

Nautilus Members enjoy an ad-free experience. Log in or Join now .

These reports were alive with vivid details: how someone’s vision loss affected their golf game or their smoking habit, their work or their love life. They were all tragedies: Each ended with an autopsy, a patient’s brain dissected to discover where, exactly, the problem lay, to inch closer to an understanding of the geography of the soul. To write these case studies, neurologists awaited the deaths and brains of living patients, robbing their subjects of the ability to choose what would become of their own bodies—the ability to write the endings of their own stories—after they had already been sapped of agency by their illnesses.

Among these case reports was one from a forbidding state hospital in the north of Moscow: the story of a 19th-century Russian journalist referred to simply as “a learned man.” The journalist suffered a type of alcoholic dementia because of the brandy he often drank to cure his writer’s block and he developed a profound amnesia. He could not remember where he was or why. He could win a game of checkers but would forget that he had even played the minute the game ended. In the place of these lost memories, the journalist’s imagination spun elaborate narratives; he believed he had written an article when in fact he had barely begun to conceive it before he became sick, would describe the prior day’s visit to a far-off place when in actuality he had been too weak to get out of bed, and maintained that some of his possessions—kept in a hospital safe—had been taken from him as part of an elaborate heist. 

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

Sacks’ journals suggest he injected his own experiences into the stories of his patients.

In the years since I first read about the journalist, I have become a neurologist, well versed in the medical jargon that describes symptoms like his: confabulation, a gap in memory filled with a story that feels entirely true to the person telling it. Confabulations can be fantastical or banal, grounded in memory or imagination, but confabulations share one essential feature: Confabulators experience their own stories as the truth. A confabulation is not a conscious lie, but rather an unconscious repair.

Neurologist Oliver Sacks, who died in 2015, was perhaps the most prolific chronicler of symptoms like confabulation, filling the pages of his books with detailed descriptions of his own patients’ wounds and blindnesses. I first read Sacks as a college student studying cognitive science and again as a neurology resident steeped in the strangeness and wonder of wounded brains. In his foreword to Awakenings, the stories of patients who had survived the “sleeping sickness” epidemic of the 1920s, alive but lethargic and permanently immobilized, Sacks wrote that the book was possible in large part because of the Bronx hospital where he practiced, which he called “a chronic hospital, an asylum,” where his patients resided for decades.

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

Sacks bore witness to “situations virtually unknown, almost unimaginable, to the general public and, indeed, to many of my colleagues.” Years after I first read Awakenings, I wrote my own book, The Mind Electric, informed in part by my own experiences at a city safety-net hospital in Boston, where I now practice neurology. I admired Sacks because he found inspiration in places others had not thought to look, because he centered stories from the margins that had previously gone untold. I wanted to do the same.

STORIES KEEP US ALIVE: “I loved The Arabian Nights as a child because it felt fantastical,” writes author and neurologist Pria Anand. “From Scheherazade, I learned that stories keep us alive.” Credit: Mutualart / Wikimedia Commons.

Among the chapters of Sacks’ 1985 The Man Who Mistook His Wife for a Hat, a collection of medical tales, is a case study titled “A Matter of Identity.” It’s the story of William Thompson, an ex-grocer struggling with a form of dementia born of longstanding alcoholism. Thompson, Sacks wrote, could not remember that he lived in a hospital. When Sacks visited him in a white doctor’s coat, Thompson imagined that he was a customer at his deli, then a kosher butcher, then an old gambling buddy, and then a Mobil station mechanic. Thompson, Sacks wrote, suffered a sort of “narrative frenzy … He must seek meaning, make meaning, in a desperate way, continually inventing, throwing bridges of meaning over abysses of meaninglessness, the chaos that yawns continually beneath him.”

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

In a New Yorker article published last month, journalist Rachel Aviv dissects Sacks’ own desperate quest for meaning, reporting on unpublished journals suggesting that Sacks invented patient narratives, sometimes injecting parts of his own experiences into the stories of his patients. In Awakenings, Sacks wrote that his patient, Leonard, likened his frozen body to a caged panther in a Rainer Maria Rilke poem. In fact, Sacks’ letters and notes suggest, it was Sacks, not Leonard, who identified with the poem, writing to a friend that the experience of writing his first book, Migraine, made him feel like “Rilke’s image of the caged panther, stupefied, dying, behind bars.” In a chapter of The Man Who Mistook His Wife for a Hat, Sacks wrote about a woman he called Rebecca, who blossomed despite her cognitive limitations after the death of her grandmother. In the book, Sacks reported that she joined a theater group and emerged from her grief as “a complete person.” Sacks’ journals, filled with transcriptions of his conversations with Rebecca, suggest that the reality was messier: Rebecca never joined a theater group but rather succumbed to her grief, telling Sacks that she wished she’d never been born.

What emerges from Aviv’s deeply reported work is not conscious deception, but the gravitational pull of confabulation, a tidy narrative mistaken for truth. Aviv quotes a letter Sacks wrote to his brother, Marcus, enclosed with a copy of The Man Who Mistook His Wife for a Hat. In the letter, Sacks calls the book a collection of “fairy tales,” explaining “these odd Narratives—half-report, half-imagined, half-science, half-fable, but with a fidelity of their own—are what I do, basically, to keep MY demons of boredom and loneliness and despair away.” In fact, Sacks writes, Marcus would likely call them “confabulations.”

Science has a long tradition of using neurological wounds like confabulation as windows, opportunities to catch a glimpse of the complex ways our brains work when they are whole. We understand something about the biological basis of communication from studying people bereft of language, about the underpinnings of human perception from studying people who have experienced blindness, and about the neural pathways that generate movement from studying people suffering paralysis. Even the most esoteric-seeming neurological injuries speak to universal features of our brains.

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

For patients like Thompson and the 19th-century Russian journalist with amnesia, confabulation bridges a discontinuity, stepping in when memory fails. For Sacks, deeply closeted until his 80s, Aviv suggests confabulation served a different, more poignant purpose: His stories offered a place to put those parts of his own identity that he had been forced to sublimate. In his journals, Sacks wrote that he gave the patients in his books “some of my own powers, and some of my phantasies too.” He gave his patients his own inner monologues, his own desires, projections of his own insecurities. “I write out symbolic versions of myself,” he wrote.

As a doctor, I, too, traffic in stories, hunger for coherence rather than chaos.

I have always loved Sacks best when he wrote, not about his patients’ symptoms, but his own. His early experiments with psychotropics are catalogued in Hallucinations, the symptoms of his own visual auras in Migraine, and his alienation from his own body in A Leg to Stand On, the story of how he tore his quadriceps while mountain-climbing in Norway and found himself unable to move the leg, even after a surgery to repair muscle. Sacks describes the leg as “foreign,” a part of himself that he cannot relate to.

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

Four years before Sacks died, he wrote about his own body in The Mind’s Eye, meditating on the childhood eye cancer that would eventually kill him alongside the stories of other artists and scientists who found themselves unable, in some essential way, to see. In a deeply personal chapter titled “Face-Blind,” Sacks revealed his own blindness: prosopagnosia, the inability to recognize even the most intimately familiar faces. Sacks remembered failing to recognize his own therapist five minutes after leaving an appointment and birthday parties at which he asked friends to wear name tags. Sometimes, he wrote, he apologized to his own reflection in the mirror, unable to recognize even himself. Still, he was oblivious to his prosopagnosia until late in his life, when he visited his brother in Australia for the first time in decades and recognized his own deficiency in his brother’s face-blindness.

For all Sacks knew about the ways that brains are able to hide their wounds, he had failed to acknowledge his own.

The genius of Sacks was that he insisted on centering people rather than illnesses, stories rather than jargon. His patients find ways to repair their reality rather than succumbing to their illnesses. As an epigraph to The Man Who Mistook His Wife for a Hat, Sacks chose to focus not on science, but rather fables: “To talk of diseases is a sort of Arabian Nights entertainment.” The quote is attributed to William Osler, the 19th-century internist who founded the hospital where I would train a century later.

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

I loved The Arabian Nights as a child because it felt fantastical. I read of caliphs and sorcerers, of jinn born of fire and of seas peopled with merfolk. The Arabian Nights is a strange, protean text, a shapeshifting, Russian-doll narrative of stories nested within stories to which tales have been added, subtracted, mutated over centuries and continents. The fables themselves are framed by the story of Scheherazade, the latest bride of a monstrous king who weds a new woman each night only to have her beheaded the following dawn. The night of their wedding, the resourceful and brilliant Scheherazade begs to be allowed to say goodbye to her beloved younger sister, Dunyazad, for whom she begins to weave a marvelous bedtime story while the king lies awake and listens. When dawn breaks, the tale remains unfinished, and the king, anxious for a resolution, spares Scheherazade’s life for one more night. The next night, and the next night, and the next, Scheherazade spins a web of endless stories that enthrall the king, always ending on a cliff-hanger so that he will keep her alive. From Scheherazade, I learned that stories keep us alive. But stories can also mislead.

When I was a medical student, reading the old case reports, I wondered whether writers were particularly prone to confabulation, primed to search for a coherent plot. Since becoming a physician, I have wondered even more whether doctors are particularly prone to confabulation. Medical students are taught to imagine a binary: doctor and patient, science and faith, objective truth and subjective report, us and them. Our morning rounds are an exercise in telling and retelling patients’ stories in a way that explains their illnesses, cloaked in the sense of objectivity offered by a white coat. But the stories told on these rounds are just as prone to false truths as the reports of an amnesia patient, subconsciously shaped by our priors, our communities, our own narratives. On rounds, a woman’s pain might be recast as anxiety, for instance, while a vitamin deficiency born of alcohol use might be regarded as a deserved punishment.

As a doctor, I, too, traffic in stories, hunger for coherence rather than the chaos and uncertainty that medicine and bodies often offer. In medicine, we arbitrate which stories are important and which don’t matter, which are true and which are false, as if we were omniscient rather than subjective beings, as if our training somehow excises the humanity, the personal, from our practice. In my own writing as in my medical practice, I remind myself to always leave room for uncertainty, for that which I cannot possibly know about someone else’s body, about their story.

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .

I loved Sacks for his unflinching desire to bear witness to the complexity of illness, and it pained me to read that he sometimes put his own story ahead of his patients’ realities. Hospitals are places of both ruin and miracles, heartache and wonder, the narratives they contain as spellbinding as they are messy. Sacks knew this better than any writer. And so, for all that feels profoundly, universally human about his vulnerabilities, I struggle to understand his impulse to confabulate on the page when the unvarnished truth would have been more compelling.

But Aviv’s article also left me with an unsettling revelation that transcends Sacks’ writing: not simply that Sacks revised reality, but that we all do. Confabulation is powerful precisely because it slips beneath consciousness, beneath the attention of even the keenest observers. Surrounded by a chaotic world, deluged with sights, sounds, and sensations, our brains instinctively search for narrative order, telling stories to explain away that which we cannot understand and that which we fear. All of us narrate our way through gaps, often mistaking the satisfaction of a tidy story for the truth. For all his flaws, perhaps despite himself, Sacks continues to illuminate the frailties of the human condition.

Lead image: Torley / Flickr

ADVERTISEMENT
Nautilus Members enjoy an ad-free experience. Log in or Join now .
Read the whole story
GaryBIshop
14 days ago
reply
Great read.
Share this story
Delete

The Overcomplexity of the Shadcn Radio Button

1 Comment

The other day I was asked to update the visual design of radio buttons in a web app at work. I figured it couldn't be that complicated. It's just a radio button right?

<input type="radio" name="beverage" value="coffee" />

Boom! Done. Radio buttons are a built-in HTML element. They've been around for 30 years. The browser makes it easy. Time for a coffee.

Enter Shadcn

I dug into our codebase and realized we were using two React components from Shadcn to power our radio buttons: <RadioGroup> and <RadioGroupItem>.

For those unfamiliar with Shadcn, it's a UI framework that provides a bunch of prebuilt UI components for use in your websites. Unlike traditional UI frameworks like Bootstrap, you don't import it with a script tag or npm install. Instead you run a command that copies the components into your codebase.

Here's the code that was exported from Shadcn into our project:

"use client";

import * as React from "react";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { CircleIcon } from "lucide-react";

import { cn } from "@/lib/utils";

function RadioGroup({
  className,
  ...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
  return (
    <RadioGroupPrimitive.Root
      data-slot="radio-group"
      className={cn("grid gap-3", className)}
      {...props}
    />
  );
}

function RadioGroupItem({
  className,
  ...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
  return (
    <RadioGroupPrimitive.Item
      data-slot="radio-group-item"
      className={cn(
        "border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
        className,
      )}
      {...props}
    >
      <RadioGroupPrimitive.Indicator
        data-slot="radio-group-indicator"
        className="relative flex items-center justify-center"
      >
        <CircleIcon className="fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" />
      </RadioGroupPrimitive.Indicator>
    </RadioGroupPrimitive.Item>
  );
}

export { RadioGroup, RadioGroupItem };

Woof... 3 imports and 45 lines of code. And it's importing a third party icon library just to render a circle. (Who needs CSS border-radius or the SVG <circle> element when you can add a third party dependency instead?)

All of the styling is done by the 30 different Tailwind classes in the markup. I should probably just tweak those to fix the styling issues.

But now I'm distracted, annoyed, and curious. Where's the actual <input>? What's the point of all this? Let's dig a little deeper.

Enter Radix

The Shadcn components import components from another library called Radix. For those unfamiliar with Radix, it's a UI framework that provides a bunch of prebuilt UI components...

Wait a second! Isn't that what I just said about Shadcn? What gives? Why do we need both? Let's see what the Radix docs say:

Radix Primitives is a low-level UI component library with a focus on accessibility, customization and developer experience. You can use these components either as the base layer of your design system, or adopt them incrementally.

So Radix provides unstyled components, and then Shadcn adds styles on top of that. How does Radix work? You can see for yourself on GitHub: https://github.com/radix-ui/...

This is getting even more complicated: 215 lines of React code importing 7 other files. But what does it actually do?

Taking a look in the browser

Let's look in the browser dev tools to see if we can tell what's going on.

A whole bunch of markup from browser dev tools. It renders a button wrapping a span wrapping a circle. There's also a hidden input and a whole bunch of attributes.

Okay, instead of a radio input it's rendering a button with an SVG circle inside it? Weird.

It's also using ARIA attributes to tell screen readers and other assistive tools that the button is actually a radio button.

ARIA attributes allow you to change the semantic meaning of HTML elements. For example, you can say that a button is actually a radio button. (If you wanted to do that for some strange reason.)

Interestingly, here's the First Rule of ARIA use:

If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.

Despite that, Radix is repurposing an element and adding an ARIA role instead of using a native HTML element.

Finally, the component also includes a hidden <input type="radio"> but only if it's used inside of a <form> element. Weird!

This is getting pretty complicated to just render a radio button. Why would you want to do this?

Styling radio buttons is hard (Wait, is it?)

My best guess is that Radix rebuilds the radio button from scratch in order to make it easier to style. Radio buttons used to be difficult to style consistently across browsers. But for several years we've been able to style radio buttons however we want using a few CSS tools:

  • appearance: none removes the radio button's default styling allowing us to do whatever we want.
  • We can use the ::before pseudo-element to render a "dot" inside of the unstyled radio button.
  • We can use the :checked pseudo-class to show and hide that dot depending on whether the radio button is checked.
  • border-radius: 50% makes things round.

Here's an example implementation:

input[type="radio"] {
  
  appearance: none;
  margin: 0;

  
  border: 1px solid black;
  background: white;
  border-radius: 50%;

  
  display: inline-grid;
  place-content: center;

  
  &::before {
    content: "";
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 50%;
  }

  
  &:checked::before {
    background: black;
  }
}

This doesn't require any dependencies, JavaScript, or ARIA roles. It's just an input element with some styles. (You can do the same thing with Tailwind if that's your jam.)

It does require knowledge of CSS but this isn't some arcane secret. Googling "how to style a radio button" shows several blog posts explaining these techniques. You may say this is a lot of CSS, but the Shadcn component we were using had 30 Tailwind classes!

I'm not trying to convince you to write your own component styles

Look, I get it. You've got a lot going on. You're not big on CSS. You just want to grab some prebuilt components so you can focus on the actual problem you're solving.

I totally understand why people reach for component libraries like Shadcn and I don't blame them at all. But I wish these component libraries would keep things simple and reuse the built-in browser elements where possible.

Who cares?

Web development is hard. There's inherent complexity in building quality sites that solve problems and work well across a wide range of devices and browsers.

But some things don't have to be hard. Browsers make things like radio buttons easy. Let's not overcomplicate it.

To understand how our radio buttons work I need to understand two separate component libraries and hundreds of lines of React.

Website visitors need to wait for JavaScript to load, parse, and run in order to be able to toggle a radio button. (In my testing, just adding these components added several KB of JS to a basic app.)

It's just a radio button

Why am I making such a big deal out of this? It's just a radio button.

But these small decisions add up to more complexity, more cognitive load, more bugs, and worse website performance.

We have strayed so far from the light

Look at it. It's beautiful:

<input type="radio" name="beverage" value="coffee" />

Fancy a game?

Play my free daily word puzzle, Tiled Words!

Adblock test (Why?)

Read the whole story
GaryBIshop
15 days ago
reply
We have strayed so far from the light!
Share this story
Delete

PyBites: “I’m worried about layoffs”

1 Comment

I’ve had some challenging conversations this week.

Lately, my calendar has been filled with calls from developers reaching out for advice because layoffs were just announced at their company.

Having been in their shoes myself, I could really empathise with their anxiety.

The thing is though, when we’d dig into why there was such anxiety, a common confession surfaced. It often boiled down to something like this:

“I got comfortable. I stopped learning. I haven’t touched a new framework or built anything serious in two years because things were okay.”

They were enjoying “Peace Time.”

I like to think of life in two modes: War Mode and Peace Time.

  • War Mode is chaotic. The house is on fire. You just lost your job, or your project was cancelled. Stress is high, money is tight, and uncertainty is the only certainty.
  • Peace Time is stable. The pay cheque hits every few weeks. The boss is happy. The weekends are free.

The deadly mistake most developers make is waiting for War Mode before they start training.

They wait until the severance package arrives to finally decide, “Okay, time to really learn Python/FastAPI/Cloud.”

It’s a recipe for disaster. Trying to learn complex engineering skills when you’re terrified about paying the mortgage is almost impossible. You’re just too stressed. You can’t focus which means you can’t dive into the deep building necessary to learn.

You absolutely have to train and skill up during Peace Time.

When things are boring and stable, that’s the exact moment you should be aggressive about your growth.

That’s when you have the mental bandwidth to struggle through a hard coding problem without the threat of redundancy hanging over your head. It’s the perfect time to sharpen the saw.

If you’re currently in a stable job, you’re in Peace Time. Don’t waste it.

Here’s what you need to do: 

  • Look at your schedule this week. Identify the “comfort blocks” (the times you’re coasting because you aren’t currently threatened).
  • Take 5 hours of that time this week and dedicate it to growth. This is your War Mode preparation. Build something that pushes you outside of your comfort zone. Go and learn the tool that intimidates you the most!
  • If crisis hits six months from now, you won’t be the one panicking. You’ll be the one who is ready.

Does this resonate with you? Are you guilty of coasting during Peace Time?

I know I’ve been there! (I often think back and wonder where I’d be now had I not spent so much time coasting through my life’s peaceful periods!)

Let’s get you back on track. Fill out this Portfolio Assessment form we’ve created to help you formulate your goals and ideas. We read every submission, Pybites Portfolio Assessment Tool.

Julian

This note was originally sent to our email list. Join here: https://pybit.es/newsletter

Read the whole story
GaryBIshop
15 days ago
reply
Good advice.
Share this story
Delete
Next Page of Stories