> All in One 586: April 2025

Ads

Monday, April 7, 2025

Mostly Clear today!



With a high of F and a low of 37F. Currently, it's 48F and Clear outside.

Current wind speeds: 10 from the South

Pollen: 0

Sunrise: April 7, 2025 at 06:25PM

Sunset: April 8, 2025 at 07:21AM

UV index: 0

Humidity: 40%

via https://ift.tt/Qm7fEbC

April 8, 2025 at 10:02AM

Feeling Like I Have No Release: A Journey Towards Sane Deployments

When I was young and dinosaurs walked the earth, I worked on a software team that developed a web-based product for two years before ever releasing it. I don’t just mean we didn’t make it publicly available; we didn’t deploy it anywhere except for a test machine in the office, accessed by two internal testers, and this required a change to each tester’s hosts file. You don’t have to be an agile evangelist to spot the red flag. There’s “release early, release often,” which seemed revolutionary the first time I heard it after living under a waterfall for years, or there’s building so much while waiting so long to deploy that you guarantee weird surprises in a realistic deployment, let alone when you get feedback from real users. I’m told the first deployment experience to a web farm was very special.

A tale of a dodgy deployment

Being a junior, I was spared being involved in the first deployment. But towards the end of the first three-month cycle of fixes, the team leader asked me, “Would you be available on Tuesday at 2 a.m. to come to the office and do a deployment?”

“Yep, sure, no worries.” I went home thinking what a funny dude my team leader was.

So on Tuesday at 9 a.m., I show up and say good morning to the team leader and the architect, who sit together staring at one computer. I sit down at my dev machine and start typing.

“Man, what happened?” the team leader says over the partition. “You said you’d be here at 2 a.m.”

I look at him and see he is not smiling. I say, ”Oh. I thought you were joking.”

“I was not joking, and we have a massive problem with the deployment.”

Uh-oh.

I was junior and did not have the combined forty years of engineering experience of the team leader and architect, but what I had that they lacked was a well-rested brain, so I found the problem rather quickly: It was a code change the dev manager had made to the way we handled cookies, which didn’t show a problem on the internal test server but broke the world on the real web servers. Perhaps my finding the issue was the only thing that saved me from getting a stern lecture. By the time I left years later, it was just a funny story the dev manager shared in my farewell speech, along with nice compliments about what I had achieved for the company — I also accepted an offer to work for the company again later.

Breaking news: Human beings need sleep

I am sure the two seniors would have been capable of spotting the problem under different circumstances. They had a lot working against them: Sleep deprivation, together with the miscommunication about who would be present, would’ve contributed to feelings of panic, which the outage would’ve exacerbated after they powered through and deployed without me. More importantly, they didn’t know whether the problem was in the new code or human error in their manual deployment process of copying zipped binaries and website files to multiple servers, manually updating config files, comparing and updating database schemas — all in the wee hours of the morning.

They were sleepily searching for a needle in a haystack of their own making. The haystack wouldn’t have existed if they had a proven automated deployment process, and if they could be sure the problem could only reside in the code they deployed. There was no reason everything they were doing couldn’t be scripted. They could’ve woken up at 6 a.m. instead of 2 a.m. to verify the automated release of the website before shifting traffic to it and fix any problems that became evident in their release without disrupting real users. The company would get a more stable website and the expensive developers would have more time to focus on developing.

If you manually deploy overnight, and then drive, you’re a bloody idiot

The 2 a.m. deployments might seem funny if it wasn’t your night to attend and if you have a dark sense of humor. In the subsequent years, I attended many 2 a.m. deployments to atone for the one I slept through. The company paid for breakfast on those days, and if we proved the deployment was working, we could leave for the day and catch up on sleep, assuming we survived the drive home and didn’t end up sleeping forever.

The manual deployment checklist was perpetually incomplete and out-of-date, yet the process was never blamed for snafus on deployment days. In reality, sometimes it was a direct consequence of the fallibility of manually working from an inaccurate checklist. Sometimes manual deployment wasn’t directly the culprit, but it made pinpointing the problem or deciding whether to roll back unnecessarily challenging. And you knew rolling back would mean forgoing your sleep again the next day so you’d have a mounting sleep debt working against you.

I learned a lot from that team and the complex features I had the opportunity to build. But the deployment process was a step backward from my internship doing Windows programming because in that job I had to write installers so my code would work on user machines, which by nature of the task, I didn’t have access to. When web development removes that inherent limitation, it’s like a devil on your shoulder tempting you to do what seems easy in the moment and update production from your dev machine. You know you want to, especially when the full deployment process is hard and people want a fix straightaway. This is why if you automate deployments, you want to lock things down so that the automated process is the only way to deploy changes.

As I became more senior and had more say in how these processes happened at my workplace, I started researching — and I found it easy to relate to the shots taken at manual deployers, such as this presentation titled “Octopus Deploy and how to stop deploying like an idiot” and Octopus Deploy founder Paul Stovell’s sentiments on how to deploy database updates: “Your database isn’t in source control? You don’t deserve one. Go use Excel.” This approach to giving developers a kick in their complacency reminds me of the long-running anti-drunk driving ads here in Australia with the slogan “If you drink then drive, you’re a bloody idiot,” which scared people straight by insulting them for destructive life choices.

In the “Stop deploying like an idiot” talk, Damian Brady insults a hypothetical deployment manager at Acme Corp named Frank, who keeps being a hero by introducing risk and wasted time to a process that could be automated using Octopus, which would never make stupid mistakes like overwriting the config file.

“Frank’s pretty proud of his process in general,” says Damian. “Frank’s an idiot.”

Why are people like this?

Frankly, some of the Franks I have worked with were something worse than idiots. Comedian Jim Jeffries has a bit in which he says he’d take a nice idiot over a clever bastard. Frank’s a cunning bastard wolf in idiotic sheep’s clothing — the demographic of people who work in software shows above average IQ, and a person appointed “deployment manager” will have googled the options to make this task easier, but he chose not to use them. The thing is, Frank gets to seem important, make other devs look and feel stupid when they try to follow his process while he’s on leave, and even when he is working he gets to take overseas trips to hang with clients because he is the only one who can get the product working on a new server. Companies must be careful which behaviors they reward, and Conway’s law applies to deployment processes.

What I learned by being forced to do deployments manually

To an extent, the process reflecting hierarchy and division of responsibility is normal and necessary, which is why Octopus Deploy has built-in manual intervention and approval steps. But also, some of the motivations to stick with manual deployments are nonsense. Complex manual deployments are still more widespread than they need to be, which makes me feel bad for the developers who still live like me back in the 2000s — if you call that living.

I guess there is an argument for the team-building experiences in those 2 a.m. deployments, much like deployments in the military sense of the word may teach the troops some valuable life lessons, even if the purported reason for the battle isn’t the real reason, and the costs turn out to be higher than anyone expects.

It reminds me of a tour I had the good fortune to take in 2023 of the Adobe San Jose offices, in which a “Photoshop floor” includes time capsule conference rooms representing different periods in Photoshop’s history, including a 90’s room with a working Macintosh Classic running Photoshop 1.0. The past is an interesting and instructive place to visit but not somewhere you’d want to live in 2025.

Even so, my experience of Flintsones-style deployments gave me an appreciation for the ways a tool like Octopus Deploy automates everything I was forced to do manually in the past, which kept my motivation up when I was working through the teething problems once I was tasked with transforming a manual deployment process into an automated process. This appreciation for the value proposition of a tool like Octopus Deploy was why I later jumped at the opportunity to work for Octopus in 2021.

What I learned working for Octopus Deploy

The first thing I noticed was how friendly the devs were and the smoothness of the onboarding process, with only one small manual change to make the code run correctly in Docker on my dev box. The second thing I noticed was that this wasn’t heaven, and there were flaky integration tests, slow builds, and cake file output that hid the informative build errors. In fairness, at the time Octopus was in a period of learning how to upscale. There was a whole project I eventually joined to performance-tune the integration tests and Octopus itself. As an Octopus user, the product had seemed as close to magic as we were likely to find, compared to the hell we had to go through without a proper deployment tool. Yet there’s something heartening about knowing nobody has a flawless codebase, and even Octopus Deploy has some smelly code they have to deal with and suboptimal deployments of some stuff.

Once I made my peace with the fact that there’s no silver bullet that magically and perfectly solves any aspect of software, including deployments, my hot take is that deploying like an idiot comes down to a mismatch between the tools you use to deploy and the reward in complexity reduced versus complexity added. Therefore, one example of deploying like an idiot is the story I opened with, in which team members routinely drove to the office at 2 a.m. to manually deploy a complicated website involving database changes, background processes, web farms, and SLAs. But another example of deploying like an idiot might be a solo developer with a side project who sets up Azure Devops to push to Octopus Deploy and pays more than necessary in money and cognitive load. Indeed, Octopus is a deceptively complex tool that can automate anything, not only deployments, but the complexity comes at the price of a learning curve and the risk of decision fatigue.

For instance, when I used my “sharpening time” (the Octopus term for side-project time) to explore ways to deploy a JavaScript library, I found at least two different ways to do it in Octopus, depending on whether it’s acceptable to automate upgrading all your consumers to the latest version of your library or whether you need more control of versioning per consumer. Sidenote: the Angry Birds Octopus parody that Octopus marketing created to go with my “consumers of your JavaScript library as tenants” article was a highlight of my time at Octopus — I wish we could have made it playable like a Google Doodle.

Nowadays I see automation as a spectrum for how automatic and sophisticated you need things to be, somewhat separate from the choice of tools. The challenge is locating that sweet spot, where automation makes your life easier versus the cost of licensing fees and the time and energy you need to devote to working on the deployment process. Octopus Deploy might be at one end of the spectrum of automated deployments when you need lots of control over a complicated automatic process. On the other end of the spectrum, the guy who runs Can I Use found that adopting git-ftp was a life upgrade from manually copying the modified files to his web server while keeping his process simple and not spending a lot of energy on more sophisticated deployment systems. Somewhere in the middle reside things like Bitbucket Pipelines or GitHub Actions, which are more automated and sophisticated than just git-ftp from your dev machine, but less complicated than Octopus together with TeamCity, which could be overkill on a simple project.

The complexity of deployment might be something to consider when defining your architecture, similar to how planning poker can trigger a business to rethink the value of certain features once they obtain holistic feedback from the team on the overall cost. For instance, you might assume you need a database, but when you factor in the complexity it adds to roll-outs, you may be motivated to rethink whether your use case truly needs a database.

What about serverless? Does serverless solve our problems given it’s supposed to eliminate the need to worry about how the server works?

Reminder: Serverless isn’t serverless

It should be uncontroversial to say that “serverless” is a misnomer, but how much this inaccuracy matters is debatable. I’ll give this analogy for why I think the name “serverless” is a problem: Early cars had a right to call themselves “horseless carriages” because they were a paradigm shift that meant your carriage could move without a horse. “Driverless cars” shouldn’t be called that, because they don’t remove the need for a driver; it’s just that the driver is an AI. “Self-driving car” is therefore a better name. Self-driving cars often work well, but completely ignoring the limitations of how they work can be fatal. When you unpack the term “serverless,” it’s like a purportedly horseless carriage still pulled by horse — but the driver claims his feeding and handling of the horse will be managed so well, the carriage will be so insulated from neighing and horse flatulence, passengers will feel as if the horse doesn’t exist. My counterargument is that the reality of the horse is bound to affect the passenger experience sooner or later.

For example, one of my hobby projects was a rap battle chat deployed to Firebase. I needed the Firebase cloud function to calculate the score for each post using the same rhyme detection algorithm I used to power the front end. This worked fine in testing when I ran the Firebase function using the Cloud Functions emulator — but it performed unacceptably after my first deployment due to a cold start (loading the pronunciation dictionary was the likely culprit if you’re wondering). Much like my experiences in the 2000s, my code behaved dramatically differently on my dev machine than on the real Firebase, almost as though there is still a server I can’t pretend doesn’t exist — but now I had limited ability to tweak it. One way to fix it was to throw money at the problem.

That serverless experience reminds me of a scene in the science fiction novel Rainbows End in which the curmudgeonly main character cuts open a car that isn’t designed to be serviced, only to find that all the components inside are labeled “No user-serviceable parts within.” He’s assured that even if he could cut open those parts, the car is “Russian dolls all the way down.” One of the other characters asks him: “Who’d want to change them once they’re made? Just trash ’em if they’re not working like you want.”

I don’t want to seem like a curmudgeon — but my point is that while something like Firebase offers many conveniences and can simplify deployment and configuration, it can also move the problem to knowing which services are appropriate to pay extra for. And you may find your options are limited when things go wrong with a deployment or any other part of web development.

Deploying this article

Since I love self-referential twist endings, I’ll point out that even publishing an article like this has a variety of possible “deployment processes.” For instance, Octopus uses Jekyll for their blog. You make a branch with the markdown of your proposed blog post, and then marketing proposes changes before setting a publication date and merging. The relevant automated process will handle publication from there. This process has the advantage of using familiar tools for collaborating on changes to a file — but it might not feel approachable to teams not comfortable with Git, and it also might not be immediately apparent how to preview the final article as it will appear on the website.

On the other hand, when I create an article for CSS-Tricks, I use Dropbox Paper to create my initial draft, then send it to Geoff Graham, who makes edits, for which I get notifications. Once we have confirmed via email that we’re happy with the article, he manually ports it to Markdown in WordPress, then sends me a link to a pre-live version on the site to check before the article is scheduled for publication. It’s a manual process, so I sometimes find problems even in this “release” of static content collaborated by only two people — but you gotta weigh how much risk there is of mistakes against how much value there would be in fully automating the process. With anything you have to publish on the web, keep searching for that sweet spot of elegance, risk, and the reward-to-effort ratio.


Feeling Like I Have No Release: A Journey Towards Sane Deployments originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/8XJefOv
via IFTTT

Sunday, April 6, 2025

Clear today!



With a high of F and a low of 29F. Currently, it's 39F and Clear outside.

Current wind speeds: 3 from the Northwest

Pollen: 0

Sunrise: April 6, 2025 at 06:27PM

Sunset: April 7, 2025 at 07:20AM

UV index: 0

Humidity: 44%

via https://ift.tt/xjTFelB

April 7, 2025 at 10:02AM

Saturday, April 5, 2025

Clear today!



With a high of F and a low of 20F. Currently, it's 30F and Clear outside.

Current wind speeds: 5 from the North

Pollen: 0

Sunrise: April 5, 2025 at 06:28PM

Sunset: April 6, 2025 at 07:20AM

UV index: 0

Humidity: 60%

via https://ift.tt/jOZfs3D

April 6, 2025 at 10:02AM

Friday, April 4, 2025

Partly Cloudy/Wind today!



With a high of F and a low of 19F. Currently, it's 28F and Cloudy/Wind outside.

Current wind speeds: 22 from the North

Pollen: 0

Sunrise: April 4, 2025 at 06:30PM

Sunset: April 5, 2025 at 07:19AM

UV index: 0

Humidity: 100%

via https://ift.tt/hlUEuL2

April 5, 2025 at 10:02AM

A New “Web” Readiness Report

Thursday, April 3, 2025

Light Rain/Fog today!



With a high of F and a low of 29F. Currently, it's 38F and Cloudy outside.

Current wind speeds: 11 from the Northeast

Pollen: 0

Sunrise: April 3, 2025 at 06:32PM

Sunset: April 4, 2025 at 07:18AM

UV index: 0

Humidity: 88%

via https://ift.tt/iX5b9q2

April 4, 2025 at 10:02AM

Wednesday, April 2, 2025

Partly Cloudy today!



With a high of F and a low of 27F. Currently, it's 35F and Partly Cloudy outside.

Current wind speeds: 8 from the North

Pollen: 0

Sunrise: April 2, 2025 at 06:33PM

Sunset: April 3, 2025 at 07:16AM

UV index: 0

Humidity: 52%

via https://ift.tt/ofy1JuH

April 3, 2025 at 10:02AM

SMIL on?

I was chatting with Andy Clarke the other day about a new article he wants to write about SVG animations.

“I’ve read some things that said that SMIL might be a dead end.” He said. “Whaddya think?”

That was my impression, too. Sarah Drasner summed up the situation nicely way back in 2017:

Unfortunately, support for SMIL is waning in WebKit, and has never (nor will likely ever) exist for Microsoft’s IE or Edge browsers. 

Chrome was also in on the party and published an intent to deprecate SMIL, citing work in other browsers to support SVG animations in CSS. MDN linked to that same thread in its SMIL documentation when it published a deprecation warning.

Well, Chrome never deprecated SMIL. At least according to this reply in the thread dated 2023. And since then, we’ve also seen Microsoft’s Edge adopt a Chromium engine, effectively making it a Chrome clone. Also, last I checked, Caniuse reports full support in WebKit browsers.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
5 4 11 79 6

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
134 136 3 6.0-6.1

Now, I’m not saying that SMIL is perfectly alive and well. It could still very well be in the doldrums, especially when there are robust alternatives in CSS and JavaScript. But it’s also not dead in the water.


SMIL on? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/PF3Ha9z
via IFTTT

Tuesday, April 1, 2025

Cloudy today!



With a high of F and a low of 30F. Currently, it's 37F and Mostly Cloudy outside.

Current wind speeds: 16 from the Northwest

Pollen: 0

Sunrise: April 1, 2025 at 06:35PM

Sunset: April 2, 2025 at 07:16AM

UV index: 0

Humidity: 92%

via https://ift.tt/o2HusM0

April 2, 2025 at 10:02AM

Crafting Strong DX With Astro Components and TypeScript

I’m a big fan of Astro’s focus on developer experience (DX) and the onboarding of new developers. While the basic DX is strong, I can easily make a convoluted system that is hard to onboard my own developers to. I don’t want that to happen.

If I have multiple developers working on a project, I want them to know exactly what to expect from every component that they have at their disposal. This goes double for myself in the future when I’ve forgotten how to work with my own system!

To do that, a developer could go read each component and get a strong grasp of it before using one, but that feels like the onboarding would be incredibly slow. A better way would be to set up the interface so that as the developer is using the component, they have the right knowledge immediately available. Beyond that, it would bake in some defaults that don’t allow developers to make costly mistakes and alerts them to what those mistakes are before pushing code!

Enter, of course, TypeScript. Astro comes with TypeScript set up out of the box. You don’t have to use it, but since it’s there, let’s talk about how to use it to craft a stronger DX for our development teams.

Watch

I’ve also recorded a video version of this article that you can watch if that’s your jam. Check it out on YouTube for chapters and closed captioning.

Setup

In this demo, we’re going to use a basic Astro project. To get this started, run the following command in your terminal and choose the “Minimal” template.

npm create astro@latest

This will create a project with an index route and a very simple “Welcome” component. For clarity, I recommend removing the <Welcome /> component from the route to have a clean starting point for your project.

To add a bit of design, I’d recommend setting up Tailwind for Astro (though, you’re welcome to style your component however you would like including a style block in the component).

npx astro add tailwind

Once this is complete, you’re ready to write your first component.

Creating the basic Heading component

Let’s start by defining exactly what options we want to provide in our developer experience.

For this component, we want to let developers choose from any HTML heading level (H1-H6). We also want them to be able to choose a specific font size and font weight — it may seem obvious now, but we don’t want people choosing a specific heading level for the weight and font size, so we separate those concerns.

Finally, we want to make sure that any additional HTML attributes can be passed through to our component. There are few things worse than having a component and then not being able to do basic functionality later.

Using Dynamic tags to create the HTML element

Let’s start by creating a simple component that allows the user to dynamically choose the HTML element they want to use. Create a new component at ./src/components/Heading.astro.

---
// ./src/component/Heading.astro
const { as } = Astro.props;
const As = as;
---

<As>
  <slot />
</As>

To use a prop as a dynamic element name, we need the variable to start with a capital letter. We can define this as part of our naming convention and make the developer always capitalize this prop in their use, but that feels inconsistent with how most naming works within props. Instead, let’s keep our focus on the DX, and take that burden on for ourselves.

In order to dynamically register an HTML element in our component, the variable must start with a capital letter. We can convert that in the frontmatter of our component. We then wrap all the children of our component in the <As> component by using Astro’s built-in <slot /> component.

Now, we can use this component in our index route and render any HTML element we want. Import the component at the top of the file, and then add <h1> and <h2> elements to the route.

---
// ./src/pages/index.astro
import Layout from '../layouts/Layout.astro';
import Heading from '../components/Heading.astro';

---

<Layout>
  <Heading as="h1">Hello!</Heading>
  <Heading as="h2">Hello world</Heading>
</Layout>
Showing the h1 and h3 elements inspected in DevTools.

This will render them correctly on the page and is a great start.

Adding more custom props as a developer interface

Let’s clean up the element choosing by bringing it inline to our props destructuring, and then add in additional props for weight, size, and any additional HTML attributes.

To start, let’s bring the custom element selector into the destructuring of the Astro.props object. At the same time, let’s set a sensible default so that if a developer forgets to pass this prop, they still will get a heading.

---
// ./src/component/Heading.astro
const { as: As="h2" } = Astro.props;
---

<As>
  <slot />
</As>

Next, we’ll get weight and size. Here’s our next design choice for our component system: do we make our developers know the class names they need to use or do we provide a generic set of sizes and do the mapping ourselves? Since we’re building a system, I think it’s important to move away from class names and into a more declarative setup. This will also future-proof our system by allowing us to change out the underlying styling and class system without affecting the DX.

Not only do we future proof it, but we also are able to get around a limitation of Tailwind by doing this. Tailwind, as it turns out can’t handle dynamically-created class strings, so by mapping them, we solve an immediate issue as well.

In this case, our sizes will go from small (sm) to six times the size (6xl) and our weights will go from “light” to “bold”.

Let’s start by adjusting our frontmatter. We need to get these props off the Astro.props object and create a couple objects that we can use to map our interface to the proper class structure.

---
// ./src/component/Heading.astro

const weights = {
    "bold": "font-bold",
    "semibold": "font-semibold",
    "medium": "font-medium",
    "light": "font-light"
}
const sizes= {
    "6xl": "text-6xl",
    "5xl": "text-5xl",
    "4xl": "text-4xl",
    "3xl": "text-3xl",
    "2xl": "text-2xl",
    "xl": "text-xl",
    "lg": "text-lg",
    "md": "text-md",
    "sm": "text-sm"
}

const { as: As="h2", weight="medium", size="2xl" } = Astro.props;
---

Depending on your use case, this amount of sizes and weights might be overkill. The great thing about crafting your own component system is that you get to choose and the only limitations are the ones you set for yourself.

From here, we can then set the classes on our component. While we could add them in a standard class attribute, I find using Astro’s built-in class:list directive to be the cleaner way to programmatically set classes in a component like this. The directive takes an array of classes that can be strings, arrays themselves, objects, or variables. In this case, we’ll select the correct size and weight from our map objects in the frontmatter.

---
// ./src/component/Heading.astro

const weights = {
  bold: "font-bold",
  semibold: "font-semibold",
  medium: "font-medium",
  light: "font-light",
};
const sizes = {
  "6xl": "text-6xl",
  "5xl": "text-5xl",
  "4xl": "text-4xl",
  "3xl": "text-3xl",
  "2xl": "text-2xl",
  xl: "text-xl",
  lg: "text-lg",
  md: "text-md",
  sm: "text-sm",
};

const { as: As = "h2", weight = "medium", size = "2xl" } = Astro.props;
---

<As class:list={[
  sizes[size], 
  weights[weight]
]}
>
  <slot />
</As>

Your front-end should automatically shift a little in this update. Now your font weight will be slightly thicker and the classes should be applied in your developer tools.

Showing the h1 and h3 elements inspected in DevTools with the relevant classnames applied.

From here, add the props to your index route, and find the right configuration for your app.

---
// ./src/pages/index.astro
import Layout from '../layouts/Layout.astro';
import Heading from '../components/Heading.astro';
---

<Layout>
  <Heading as="h1" size="6xl" weight="light">Hello!</Heading>
  <Heading as="h3" size="xl" weight="bold">Hello world</Heading>
</Layout>
Showing the h1 and h3 elements inspected in DevTools revealing the applied classes.

Our custom props are finished, but currently, we can’t use any default HTML attributes, so let’s fix that.

Adding HTML attributes to the component

We don’t know what sorts of attributes our developers will want to add, so let’s make sure they can add any additional ones they need.

To do that, we can spread any other prop being passed to our component, and then add them to the rendered component.

---
// ./src/component/Heading.astro

const weights = {
  // etc.
};
const sizes = {
  // etc.
};

const { as: As = "h2", weight = "medium", size = "md", ...attrs } = Astro.props;
---

<As class:list={[
  sizes[size], 
  weights[weight]
]}
{...attrs}
>
  <slot />
</As>

From here, we can add any arbitrary attributes to our element.

---
// ./src/pages/index.astro
import Layout from '../layouts/Layout.astro';
import Heading from '../components/Heading.astro';

---

<Layout>
  <Heading id="my-id" as="h1" size="6xl" weight="light">Hello!</Heading>
  <Heading class="text-blue-500" as="h3" size="xl" weight="bold">Hello world</Heading>
</Layout>

I’d like to take a moment to truly appreciate one aspect of this code. Our <h1>, we add an id attribute. No big deal. Our <h3>, though, we’re adding an additional class. My original assumption when creating this was that this would conflict with the class:list set in our component. Astro takes that worry away. When the class is passed and added to the component, Astro knows to merge the class prop with the class:list directive and automatically makes it work. One less line of code!

Showing the h1 and h3 elements inspected in DevTools.

In many ways, I like to consider these additional attributes as “escape hatches” in our component library. Sure, we want our developers to use our tools exactly as intended, but sometimes, it’s important to add new attributes or push our design system’s boundaries. For this, we allow them to add their own attributes, and it can create a powerful mix.

It looks done, but are we?

At this point, if you’re following along, it might feel like we’re done, but we have two issues with our code right now: (1) our component has “red squiggles” in our code editor and (2) our developers can make a BIG mistake if they choose.

The red squiggles come from type errors in our component. Astro gives us TypeScript and linting by default, and sizes and weights can’t be of type: any. Not a big deal, but concerning depending on your deployment settings.

The other issue is that our developers don’t have to choose a heading element for their heading. I’m all for escape hatches, but only if they don’t break the accessibility and SEO of my site.

Imagine, if a developer used this with a div instead of an h1 on the page. What would happen?We don’t have to imagine, make the change and see.

Showing the div and h3 elements inspected in DevTools.

It looks identical, but now there’s no <h1> element on the page. Our semantic structure is broken, and that’s bad news for many reasons. Let’s use typing to help our developers make the best decisions and know what options are available for each prop.

Adding types to the component

To set up our types, first we want to make sure we handle any HTML attributes that come through. Astro, again, has our backs and has the typing we need to make this work. We can import the right HTML attribute types from Astro’s typing package. Import the type and then we can extend that type for our own props. In our example, we’ll select the h1 types, since that should cover most anything we need for our headings.

Inside the Props interface, we’ll also add our first custom type. We’ll specify that the as prop must be one of a set of strings, instead of just a basic string type. In this case, we want it to be h1h6 and nothing else.

---
// ./src/component/Heading.astro
import type { HTMLAttributes } from 'astro/types';

interface Props extends HTMLAttributes<'h1'> {
  as: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
}

//... The rest of the file
---

After adding this, you’ll note that in your index route, the <h1> component should now have a red underline for the as="div" property. When you hover over it, it will let you know that the as type does not allow for div and it will show you a list of acceptable strings.

If you delete the div, you should also now have the ability to see a list of what’s available as you try to add the string.

Showing a contextual menu that displays all of heading level options for the heading component while the code is being typed.

While it’s not a big deal for the element selection, knowing what’s available is a much bigger deal to the rest of the props, since those are much more custom.

Let’s extend the custom typing to show all the available options. We also denote these items as optional by using the ?:before defining the type.

While we could define each of these with the same type functionality as our as type, that doesn’t keep this future proofed. If we add a new size or weight, we’d have to make sure to update our type. To solve this, we can use a fun trick in TypeScript: keyof typeof.

There are two helper functions in TypeScript that will help us convert our weights and sizes object maps into string literal types:

  • typeof: This helper takes an object and converts it to a type. For instance typeof weights would return type { bold: string, semibold: string, ...etc}
  • keyof: This helper function takes a type and returns a list of string literals from that type’s keys. For instance keyof type { bold: string, semibold: string, ...etc} would return "bold" | "semibold" | ...etc which is exactly what we want for both weights and sizes.
---
// ./src/component/Heading.astro
import type { HTMLAttributes } from 'astro/types';

interface Props extends HTMLAttributes<'h1'> {
  as: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
  weight?: keyof typeof weights;
  size?: keyof typeof sizes;
}

// ... The rest of the file

Now, when we want to add a size or weight, we get a dropdown list in our code editor showing exactly what’s available on the type. If something is selected, outside the list, it will show an error in the code editor helping the developer know what they missed.

Showing a contextual menu that displays all of the size options for the heading component while the code is being typed.

While none of this is necessary in the creation of Astro components, the fact that it’s built in and there’s no additional tooling to set up means that using it is very easy to opt into.

I’m by no means a TypeScript expert, but getting this set up for each component takes only a few additional minutes and can save a lot of time for developers down the line (not to mention, it makes onboarding developers to your system much easier).


Crafting Strong DX With Astro Components and TypeScript originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://ift.tt/HrTLGFB
via IFTTT

Mostly Clear today!

With a high of F and a low of 37F. Currently, it's 48F and Clear outside. Current wind speeds: 10 from the South Pollen: 0 Sunri...