Hi, I'm Anh. You might know me better by my pseudonym: pwign.

I'm a designer from Hanoi, Vietnam. I mostly tweet and write about design, development, software, and tech in general. Most of my design work are currently on Dribbble. When I'm not designing, I take photos for my Unsplash page.

If you'd like to reach out, my Twitter DM is open — or you can also email me.

This website is currently under construction as I'm working on its design. Please let me know if anything breaks.



Since reach is an important aspect for every blog, I want to make sure my social card previews are displayed properly and prominently when someone shares my post.

I currently have a boilerplate social image for all of the blog posts.

Current boilerplate implementation for blog posts

It works well, but not well enough for my liking. I want something different for each blog post, and the thumbnail should provide something useful other than branding purposes.


The design should be simple because I don’t like over complicating things more than they need to be. The most common layout I have seen with Open Graph images in personal blogs includes a logo, some background patterns, a title, an excerpt, and a publish date. But we’re not going to do all that.

The only reason why I want an Open Graph social image is that I want the tappable area to be larger than an ordinary link.

Small Twitter card design
Large Twitter card design

Looking at the current Facebook and Twitter card design for links, the title, website domain and excerpt are already included in the card description.

The information shown on Twitter and Facebook card designs

The data I plan on repeating in the image design is the title. Because when people are looking at an article in their timeline, they’re reading, and so you need to give people things to read. Now we know we’re only working with:

  1. A logo
  2. The title of the article

It was time to figure out all the technicalities. The first thing that comes to mind is the image size. Thankfully, someone on the internet already did the research. It seems like the perfect ratio is 1.9:1, and the size is 1200 x 630. I’ll double the image size just in case, and because I have a Retina display, I like my images crisp.

Something I noticed when I was doing the mockup for the image is that sometimes, the text would flow one or two words onto a second line, which looks absolutely horrendous.

Long blog post title leaving a widow

I can reduce the text size so that the word “website” would fit on a single line. Or I can just resize the text box so that the line will break where it doesn’t leave any widows.

I figured that once again someone on the internet probably already tried to solve this. And they did. The people at Adobe made BalanceText to solve this exact problem, which works perfectly for my use case.

I also need to account for very long titles. When that happens, the title will max out at 3 lines, and an ellipsis will be added if necessary.

Long blog post title with an ellipsis


After looking at a few options, gatsby-plugin-printer is a perfect fit for how I want to set it up. It’s worth noting that the only version of the plugin that works for me is 1.0.8.


Install with npm:

npm install --save gatsby-plugin-printer@1.0.8

Install with yarn:

yarn add gatsby-plugin-printer@1.0.8


After installing, add gatsby-plugin-printer to your gatsby-config.js file.

module.exports = {
  plugins: [`gatsby-plugin-printer`],

The Gatsby plugin works by taking a screenshot of a component with our data, so we need to create a component to handle that.

import React from "react"

export default () => {
  return (
        width: 2400,
        height: 1260,
        backgroundColor: "black",
        color: "white",

The size of the wrapper <div></div> will be the size of the screenshot, and so I just had to put in 2400 and 1260, respectively.

Then we need a way to feed our title into the printer component. We can do that from our gatsby-node.js file.

exports.onCreateNode = ({ node }) => {
  // Get the current path of the blog file and return the slug and the content type
  let filePathSplit = node.fileAbsolutePath.split("/")
  let contentType = filePathSplit[filePathSplit - 3] // "~/content/blog/og-image/index.tsx" --> "blog"
  let fileName = filePathSplit[filePathSplit.length - 2] // "~/content/blog/og-image/index.tsx" --> "og-image"

  // Check if the content type is a blog post
  if (node.internal.type === "Mdx" && contentType === "blog") {
    // Start the "printing" job to generate the OG Image
      fileName: fileName, // the file name of the generated image, which is set to match my blog post slug
      outputDir: "images/blog/", // the location as to where the OG image will be stored, relative to the /public/ folder
      data: {
        // Any data that needs to be fed into the printer component
        title: node.frontmatter.title,
      component: require.resolve("./src/components/seo/BlogThumbnail.js"), // The printer component

Now we pass the data into the printer component.

import React from "react"

export default ({ title }) => {
  return (
        width: 2400,
        height: 1260,
        backgroundColor: "black",
        color: "white",



To install BalanceText, I just need to install the React plugin.

Install with npm:

npm install react-balance-text

Install with yarn:

yarn add react-balance-text


Then I can wrap the text element I needed to be balanced with <BalanceText></BalanceText> and add additional styling.

import React from "react"
import BalanceText from "react-balance-text"

export default ({ title }) => {
  const styles = `
    @font-face {
      // Setup the font here

    * {
      font-family: Unica77;

  return (
        width: 2400,
        height: 1260,
        padding: 160,
        display: "flex",
        flexDirection: "column",
        justifyContent: "space-between",
        boxSizing: "border-box",
        overflowX: "hidden",
        backgroundColor: "black",
        color: "white",
      <style type="text/css" scoped>
      <svg width="134" height="123" xmlns="">
        // Logo image
          fontWeight: 500,
          fontSize: "11rem",
          lineHeight: 1.2,
          margin: 0,
          width: "100%",

Further reading

Published · Hyperlink


I’m currently designing and developing this website, so I thought it would be fun to write about my process.

Something that came up most recently in my to-do list is choosing the right colors for the website. I knew from the beginning, I wanted to support system-wide dark mode—and the easiest way to do that is with CSS variables.

// Color to be used in default mode (or light mode).
:root {
  --color-text-60: #636363;

// Color to be used in dark mode.
@media (prefers-color-scheme: dark) {
  :root {
    --color-text-60: #cccccc;

If you’re already familiar with CSS variables, you know there’s a smarter way to do this.

// Light: --color-text-60: rgba(0, 0, 0, 0.6)
:root {
  --color-base-rgb: 0, 0, 0;
  --color-text-60: rgba(var(--color-base-rgb), 0.6);

// Dark: --color-text-60: rgba(255, 255, 255, 0.6)
@media (prefers-color-scheme: dark) {
  :root {
    --color-base-rgb: 255, 255, 255;

The problem with this approach is that even though the opacity value we’re using (0.6 or 60%) is the same for both themes, the real contrast to the human eyes isn’t.

Before — unbalanced color contrast between the two themes.

When you look at the link in the image, it appears to have less contrast against the surrounding text in the light theme. Another example of how our eyes fool us is how the same value of brightness and saturation of yellow will feel brighter than violet.

In order to fix this, the opacity value for the link in the dark theme needs to be manually raised to match the one in the light theme, or vice versa.

After — balanced color contrast between the two themes.

That’s why for white and black colors, I have to use two separate palettes so that the contrast looks uniform in both themes. Unfortunately, there’s no programmable way for this process I’ve found to be useful, so I just ended up eyeballing my way through all of them.

Thankfully, Figma’s Selection Colors made this process extremely simple.

Aside from the black and white colors, I also had a brand/primary color scheme to accent my design. It’s a mixture of blue and purple, or as some people prefer to call it “blurple.”

It went through a similar process as the black and white color scheme, except the saturation, has to be dropped way down for the dark theme.

At last, it was time to migrate to the improved color system. I made all the old values red, so I know where to start switching to the new colors.

It might seem like I am nitpicking, but it’s a gripe I have always had with websites and apps that didn’t take colors seriously enough—who changed the background to black and called it a day.

Oh, and if you’re curious about the colors used in the code blocks, it’s from the VSCode Github theme. I also didn’t forget to use CSS variables, so it should work in both light and dark mode.

Published · Hyperlink

My goal for this website is to make it as accessible as possible. Even though my content isn’t really for people outside of tech, I still want to include them in the off chance they might be interested.

One of the most popular conventions that websites have with external links is adding target="_blank" to it so that browsers will open the link in a new tab. I think the main reason why people use this is that they don’t want users to leave their website. Opening external links in a new tab mean that your website will still stay open as a tab. And users need to intentionally close off your website tab if they don’t want to continue browsing your site.

I’ve always held down the Cmd key while clicking a link to open it as a new tab in the background, so it doesn’t affect my browsing experience.


My problem with implementing target="_blank" is that there’s no indication as to whether the link to open in a new tab or not.

If you have Status Bar turned on in macOS Safari, it will tell you if a link will open in a new tab as opposed to Chrome and Firefox, which will only show the URL.

Safari status bar

But the status bar isn’t switched on by default in Safari, and it only works in macOS.

Some websites add an icon that indicates that a link is an external one, but when placed in a body of text, it’s disruptive and takes away the focus from the content, which I do not want. Also, the icon design isn’t very popular, which might confuse people who are new to the website.

Icon designs for external links

A viable option I’m considering is to add a tooltip upon hovering that indicate the link as an external one, and as a bonus, show the domain of where they will be redirected to.

Something like this:

But it doesn’t solve the problem that people don’t know the link will be opened in another tab; it’s only telling them that the link is going to move them off-site.

Back button

Another concern I have is when you open a link in a new tab. It’s not easy to get back to the origin tab. The back button is greyed out. The only web browser that seems to address this is Safari. When you open a page as a new tab there, the back button will bring you back to the origin tab.

Safari has a back button for new tabs.

But again, it means that this feature is only limited to platforms where there’s Safari—so not the best option.

Perhaps I’ll find a better solution to all of these in the future, but for now, I’m keeping target="_blank" on my website for the sake of being consistent with the rest of the web.

Published · Hyperlink

After five days using iOS 14, I have to say it feels surprisingly stable. iOS 12 beta was so unusable on my device that I decided to skip iOS 13 beta altogether.

As always, the general advice is to not install it on your primary device. But if you still want to proceed, here are some common issues I have seen for the past few days:

  • Animations skipping frames. Especially when opening and closing apps.
  • Random overheating. Have to restart to resolve.
  • Battery will drain very quickly for the first day following installation, presumably for search indexing.

Most apps seem to work fine for me, but some will be pretty annoying with the occasional clipboard access notifications.

Published · Hyperlink

The new design of the San Francisco typeface is, unfortunately, a step down in terms of legibility for users using Latin characters with diacritics like me.

Here’s a comparison of the before and after of the new update:

Diacritics in iOS 14 has a lower height than those in iOS 13
Close up view of diacritics in iOS 14

The problem isn’t the usual “find Waldo” UI nitpicking. I only found it because it was much harder for me to read, and keep in mind, I’m 18 years old.

I hope this is just an oversight on Apple’s part, but if this goes through to public release, I don’t think I can recommend the new update to my parents, considering they’re already using the enlarged system font size setting.

Apart from the size change, there’s a new design for the diacritics as well.

The WWDC session video didn’t address this change, but the description is undoubtedly ironic.

Learn how to achieve exceptional typography in your app’s user interface that enhances legibility, accessibility, and consistency across Apple platforms.


Published · Hyperlink

The iOS 13 text selection is really frustrating to use.

Touch input has always been a core part of what makes iOS great. The software is always aware that the user’s fingers are not pixel-perfect pointer like a mouse cursor. That’s why we can easily increase a tappable area with button content inset, have character preview on keyboards, and magnification loupe for text selection.

Character preview
Magnification loupe
Enlarged touch target for fingertips

I’m not saying that everyone needs something like this, and the design team at Apple most likely has done their user research to justify removing such a defining iOS feature, but as someone who considers themselves as a “power user”, it’s hard to imagine how others are coping with this change.


Published · Hyperlink

Medium is known for being one of the most loved blog platforms out there—or at least until it started to monetize, and all the growth hacks start coming left and right. If you’ve visited Medium before, you know about the obtrusive pops up and ask you to sign up, or the 100px tall stacked navigation bars.

But this isn’t about those intrusive growth hacks; this is about the most-anxiety inducing feature that was released alongside the monetization push: the free articles limit warning.

This one

So here’s an attempt to fix that.

1. Make the paywalled articles indicator more obvious compared to free ones

Paywalled articles are currently marked by a star icon. By identifying them with a “Membership” tag, they are instantly more recognizable and far more simple for people to identify the different content type at first glance.

Article item in the article list with a better indication it's a paywalled article

Since the touch target is also larger than an icon, it’s also possible to attach a tooltip to it to display more information about the Membership.

Tooltip explanation when clicked on the new paywall indicator

A downside to this is that repeated usage of the tag will make the layout look much more busy, increasing cognitive load and prevent readers from focusing on the content that matters.

The overall design in a grid feels too busy

It’s also worth noting that as I’m editing this blog post for republishing on my new website, a lot more content on Medium has been put behind the paywall. So it’s very likely that the Membership tag will frequently appear in the layout.

I have also realized that the majority of people who don’t know the meaning behind the star icons are those who have never visited Medium before, so they have probably visited the article from an external link.

As an alternative, the indication tooltip for the Membership program could be displayed on the first visit inside the article instead.

2. Remove the tripwire penalty

Twitter card of a Medium article in the Membership program

Also related to different referrer source, if you visit a Membership article from somewhere else like Twitter or Reddit, you wouldn’t know whether the article is a free one or not until you actually visit the link.

Unexpected user interface elements like these feel like stepping on an explosive mine while taking a stroll in the park. No one likes surprises, especially if you want to be a platform that people can put in their trust.

I’m not sure if the current implementation can be classified as a dark pattern. But I feel cheated because I was never warned that I have a limited amount of reading allowance before I entered the website.

There are two ways this information can be sufficiently communicated to the reader:

  • On the article preview card
  • On the article page

The first option doesn’t work for 4 reasons:

  1. Because card previews vary a lot in size, this text might be illegible in smaller sizes.
  2. Having text on an image thumbnail is generally frowned upon because it’s not very accessible.
  3. Not all links are accompanied with a card preview.
  4. Not every article has a preview image.

The second option seems to be more plausible.


First-time readers now know that the article they’re reading is behind a paywall. They’re given options as to whether they want to read the article and use up one membership preview, or they can upgrade to get unlimited access to all the articles.

Also, please don’t use Medium.

Published · Hyperlink