4 Favorite Blogs from Ecommerce Companies

Publishing a blog on an ecommerce site builds community, creates trust, and gives consumers a reason to return. Sales would likely follow.

But online merchants with long-term, quality blogs are rare. What follows are four of my favorites.

Mr Porter

Mr Porter is an online retailer of men’s fashion items, founded in 2011. The company sells roughly 300 designer brands. A companion site, Net-A-Porter, sells female fashion goods.

In 2014 Mr Porter launched “The Journal,” a weekly web magazine with a six-times-per-year print edition. In March 2020, the web version began posting daily, presumably due to its global popularity — The Journal claims 2.5 million monthly visitors.

Home page of The JournalHome page of The Journal

Mr Porter’s “The Journal” launched in 2014. It now claims 2.5 million monthly visitors worldwide.

The Journal’s posts are short and easy to absorb. The categories — Latest, Fashion, Grooming, Watches, Travel, Lifestyle — cover mostly fashion and accessories with additional topics to keep readers’ interest. All are related to Mr Porter’s products. For example, the products in the post “Every Polo Shirt You’ll Need This Summer” are on Mr Porter’s digital shelves. The items are linked in the body content.

Goop

Actress Gwyneth Paltrow founded Goop in 2008 as a weekly email newsletter providing health and wellness advice. She soon added a website followed by ecommerce. Article content remains Goop’s focus. The company now hosts a wellness summit, a celebrity cruise, a podcast, and a Netflix series.

Goop’s articles are accessible from the home page. Categories are Beauty, Food & Home, Style, Travel, and Wellness. Good PhD, a sixth category, is produced by the company’s science and research team to “compile the most significant studies and information on an array of health topics, conditions, and diseases.”

Home page of Goop.Home page of Goop.

Goop’s articles are accessible from the home page. Categories are Beauty, Food & Home, Style, Travel, Wellness, and Good PhD.

Similar to The Journal from Mr Porter, Goop’s articles link to its products. For example, “6 Reasons to Make the Shift to Mineral Sunscreen” links to six products.

Goop’s newsletter summarizes the best blog posts as well as items for purchase.

Dermstore

Dermstore was founded in 1999 by a California-based dermatologist. It’s now a popular ecommerce site for skin care products, with over 350 brands. “The Dermstore Blog” addresses mostly skin care topics from experts. Categories are Skin Care, Anti-aging, Top Product Picks, Doctors Office, Hair Care Tips, and Dermatologist Reviewed. Dermstore’s content team consists of in-house and freelance writers, often including physicians and licensed aestheticians.

Home page of The Dermstore BlogHome page of The Dermstore Blog

“The Dermstore Blog” addresses skin care topics from experts. Categories are Skin Care, Anti-aging, Top Product Picks, Doctors Office, Hair Care Tips, and Dermatologist Reviewed.

Posts on The Dermstore Blog include “Buy Now” buttons and other links to the company’s products.

If your company can’t afford expert contributors, invite them to leave their opinion on a topic and then include it in a post as a quote. I once wrote for Dermstore and landed free expert interviews and quotes through the website Help a Reporter.

Motherly

Motherly started in 2015 as a blog focused on motherhood. The company has since added two podcasts, “The Motherly Podcast” and “Becoming Mama: A Pregnancy and Birth Podcast.” Motherly says its combined audience reaches 30 million users. The company raised $5.4 million in Series A funding in 2020 and then launched a store.

Motherly remains content-centric via in-house writers and submissions from mothers and practitioners such as pediatricians, nurses, and midwives. Categories are Pregnancy, Parenting, and Life. Posts include links to (paid) classes and store products, such as cribs, jogging strollers, and car seats.

Home page of MotherlyHome page of Motherly

Motherly started in 2015 as a blog focused on motherhood. In 2020 it launched a store. The site remains content-centric — categories are Pregnancy, Parenting, and Life.

CEO Jill Koziol said that Motherly’s brand is “woman-centered — not baby-centered — and expert-driven.” It then used that brand to sell products.

Building Trust

Trust is essential for building a sustainable brand. Content, done well, is an excellent way to build trust with customers and prospects. Each company above features expert authors on important topics multiple times per week. Consumers will enjoy reading the posts even if they don’t purchase a product.

But there’s nothing wrong with including product mentions, as the companies have demonstrated. The trick is to give away worthwhile information and include product mentions that support the topic.

Global vs. Local Styling In Next.js

I have had a great experience using Next.js to manage complex front-end projects. Next.js is opinionated about how to organize JavaScript code, but it doesn’t have built-in opinions about how to organize CSS.

After working within the framework, I have found a series of organizational patterns that I believe both conform to the guiding philosophies of Next.js and exercise best CSS practices. In this article, we’ll build a website (a tea shop!) together to demonstrate these patterns.

Note: You probably will not need prior Next.js experience, although it would be good to have a basic understanding of React and to be open to learning some new CSS techniques.

Writing “Old-Fashioned” CSS

When first looking into Next.js, we may be tempted to consider using some kind of CSS-in-JS library. Though there may be benefits depending on the project, CSS-in-JS introduces many technical considerations. It requires using a new external library, which adds to the bundle size. CSS-in-JS can also have a performance impact by causing additional renders and dependencies on the global state.

Recommended reading: “The Unseen Performance Costs Of Modern CSS-in-JS Libraries In React Apps)” by Aggelos Arvanitakis

Furthermore, the whole point of using a library like Next.js is to statically render assets whenever possible, so it doesn’t make so much sense to write JS that needs to be run in the browser to generate CSS.

There are a couple of questions we have to consider when organizing style within Next.js:

How can we fit within the conventions/best practices of the framework?

How can we balance “global” styling concerns (fonts, colors, main layouts, and so on) with “local” ones (styles regarding individual components)?

The answer I have come up with for the first question is to simply write good ol’ fashioned CSS. Not only does Next.js support doing so with no additional setup; it also yields results that are performant and static.

To solve the second problem, I take an approach that can be summarized in four pieces:

  1. Design tokens
  2. Global styles
  3. Utility classes
  4. Component styles

I’m indebted to Andy Bell’s idea of CUBE CSS (“Composition, Utility, Block, Exception”) here. If you haven’t heard of this organizational principle before, I recommended checking out its official site or feature on the Smashing Podcast. One of the principles we will take from CUBE CSS is the idea that we should embrace rather than fear the CSS cascade. Let’s learn these techniques by applying them to a website project.

Getting Started

We’ll be building a tea store because, well, tea is tasty. We’ll start by running yarn create next-app to make a new Next.js project. Then, we’ll remove everything in the styles/ directory (it’s all sample code).

Note: If you want to follow along with the finished project, you can check it out here.

Design Tokens

In pretty much any CSS setup, there’s a clear benefit to storing all globally shared values in variables. If a client asks for a color to change, implementing the change is a one-liner rather than a massive find-and-replace mess. Consequently, a key part of our Next.js CSS setup will be storing all site-wide values as design tokens.

We’ll use inbuilt CSS Custom Properties to store these tokens. (If you’re not familiar with this syntax, you can check out “A Strategy Guide To CSS Custom Properties”.) I should mention that (in some projects) I’ve opted to use SASS/SCSS variables for this purpose. I haven’t found any real advantage, so I usually only include SASS in a project if I find I need other SASS features (mix-ins, iteration, importing files, and so on). CSS custom properties, by contrast, also work with the cascade and can be changed over time rather than statically compiling. So, for today, let’s stick with plain CSS.

In our styles/ directory, let’s make a new design_tokens.css file:

:root { --green: #3FE79E; --dark: #0F0235; --off-white: #F5F5F3; --space-sm: 0.5rem; --space-md: 1rem; --space-lg: 1.5rem; --font-size-sm: 0.5rem; --font-size-md: 1rem; --font-size-lg: 2rem;
}

Of course, this list can and will grow over time. Once we add this file, we need to hop over to our pages/_app.jsx file, which is the main layout for all our pages, and add:

import '../styles/design_tokens.css'

I like to think of design tokens as the glue that maintains consistency across the project. We will reference these variables on a global scale, as well as within individual components, ensuring a unified design language.

Global Styles

Next up, let’s add a page to our website! Let’s hop into the pages/index.jsx file (this is our homepage). We’ll delete all the boilerplate and add something like:

export default function Home() { return <main> <h1>Soothing Teas</h1> <p>Welcome to our wonderful tea shop.</p> <p>We have been open since 1987 and serve customers with hand-picked oolong teas.</p> </main>
}

Unfortunately, it will look quite plain, so let’s set some global styles for basic elements, e.g. <h1> tags. (I like to think of these styles as “reasonable global defaults”.) We may override them in specific cases, but they’re a good guess as to what we will want if we don’t.

I’ll put this in the styles/globals.css file (which comes by default from Next.js):

*,
*::before,
*::after { box-sizing: border-box;
} body { color: var(--off-white); background-color: var(--dark);
} h1 { color: var(--green); font-size: var(--font-size-lg);
} p { font-size: var(--font-size-md);
} p, article, section { line-height: 1.5;
} :focus { outline: 0.15rem dashed var(--off-white); outline-offset: 0.25rem;
}
main:focus { outline: none;
} img { max-width: 100%;
}

Of course, this version is fairly basic, but my globals.css file doesn’t usually end up actually needing to get too large. Here, I style basic HTML elements (headings, body, links, and so on). There is no need to wrap these elements in React components or to constantly add classes just to provide basic style.

I also include any resets of default browser styles. Occasionally, I will have some site-wide layout style to provide a “sticky footer”, for example, but they only belong here if all pages share the same layout. Otherwise, it will need to be scoped inside individual components.

I always include some kind of :focus styling to clearly indicate interactive elements for keyboard users when focused. It’s best to make it an integral part of the site’s design DNA!

Now, our website is starting to shape up:

Utility Classes

One area where our homepage could certainly improve is that the text currently always extends to the sides of the screen, so let’s limit its width. We need this layout on this page, but I imagine that we might need it on other pages, too. This is a great use case for a utility class!

I try to use utility classes sparingly rather than as a replacement for just writing CSS. My personal criteria for when it makes sense to add one to a project are:

  1. I need it repeatedly;
  2. It does one thing well;
  3. It applies across a range of different components or pages.

I think this case meets all three criteria, so let’s make a new CSS file styles/utilities.css and add:

.lockup { max-width: 90ch; margin: 0 auto;
}

Then let’s add import '../styles/utilities.css' to our pages/_app.jsx. Finally, let’s change the <main> tag in our pages/index.jsx to <main className="lockup">.

Now, our page is coming together even more. Because we used the max-width property, we don’t need any media queries to make our layout mobile responsive. And, because we used the ch measurement unit — which equates to about the width of one character — our sizing is dynamic to the user’s browser font size.

As our website grows, we can continue adding more utility classes. I take a fairly utilitarian approach here: If I’m working and find I need another class for a color or something, I add it. I don’t add every possible class under the sun — it would bloat the CSS file size and make my code confusing. Sometimes, in larger projects, I like to break things up into a styles/utilities/ directory with a few different files; it’s up to the needs of the project.

We can think of utility classes as our toolkit of common, repeated styling commands that are shared globally. They help prevent us from constantly rewriting the same CSS between different components.

Component Styles

We’ve finished our homepage for the moment, but we still need to build a piece of our website: the online store. Our goal here will be to display a card grid of all the teas we want to sell, so we’ll need to add some components to our site.

Let’s start off by adding a new page at pages/shop.jsx:

export default function Shop() { return <main> <div className="lockup"> <h1>Shop Our Teas</h1> </div> </main>
}

Then, we’ll need some teas to display. We’ll include a name, description, and image (in the public/ directory) for each tea:

const teas = [ { name: "Oolong", description: "A partially fermented tea.", image: "/oolong.jpg" }, // ...
]

Note: This isn’t an article about data fetching, so we took the easy route and defined an array at the beginning of the file.

Next, we’ll need to define a component to display our teas. Let’s start by making a components/ directory (Next.js doesn’t make this by default). Then, let’s add a components/TeaList directory. For any component that ends up needing more than one file, I usually put all the related files inside a folder. Doing so prevents our components/ folder from getting unnavigable.

Now, let’s add our components/TeaList/TeaList.jsx file:

import TeaListItem from './TeaListItem' const TeaList = (props) => { const { teas } = props return <ul role="list"> {teas.map(tea => <TeaListItem tea={tea} key={tea.name} />)} </ul>
} export default TeaList

The purpose of this component is to iterate over our teas and to show a list item for each one, so now let’s define our components/TeaList/TeaListItem.jsx component:

import Image from 'next/image' const TeaListItem = (props) => { const { tea } = props return <li> <div> <Image src={tea.image} alt="" objectFit="cover" objectPosition="center" layout="fill" /> </div> <div> <h2>{tea.name}</h2> <p>{tea.description}</p> </div> </li>
} export default TeaListItem

Note that we’re using Next.js’s built-in image component. I set the alt attribute to an empty string because the images are purely decorative in this case; we want to avoid bogging screen reader users down with long image descriptions here.

Finally, let’s make a components/TeaList/index.js file, so that our components are easy to import externally:

import TeaList from './TeaList'
import TeaListItem from './TeaListItem' export { TeaListItem } export default TeaList

And then, let’s plug it all together by adding import TeaList from ../components/TeaList and a <TeaList teas={teas} /> element to our Shop page. Now, our teas will show up in a list, but it won’t be so pretty.

Colocating Style With Components Through CSS Modules

Let’s start off by styling our cards (the TeaListLitem component). Now, for the first time in our project, we’re going to want to add style that is specific to just one component. Let’s create a new file components/TeaList/TeaListItem.module.css.

You may be wondering about the module in the file extension. This is a CSS Module. Next.js supports CSS modules and includes some good documentation on them. When we write a class name from a CSS module such as .TeaListItem, it will automatically get transformed into something more like . TeaListItem_TeaListItem__TFOk_ with a bunch of extra characters tacked on. Consequently, we can use any class name we want without being concerned that it will conflict with other class names elsewhere in our site.

Another advantage to CSS modules is performance. Next.js includes a dynamic import feature. next/dynamic lets us lazy load components so that their code only gets loaded when needed, rather than adding to the whole bundle size. If we import the necessary local styles into individual components, then users can also lazy load the CSS for dynamically imported components. For large projects, we may choose to lazy load significant chunks of our code and only to load the most necessary JS/CSS upfront. As a result, I usually end up making a new CSS Module file for every new component that needs local styling.

Let’s start by adding some initial styles to our file:

.TeaListItem { display: flex; flex-direction: column; gap: var(--space-sm); background-color: var(--color, var(--off-white)); color: var(--dark); border-radius: 3px; box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
}

Then, we can import style from ./TeaListItem.module.css in our TeaListitem component. The style variable comes in like a JavaScript object, so we can access this class-like style.TeaListItem.

Note: Our class name doesn’t need to be capitalized. I’ve found that a convention of capitalized class names inside of modules (and lowercase ones outside) differentiates local vs. global class names visually.

So, let’s take our new local class and assign it to the <li> in our TeaListItem component:

<li className={style.TeaListComponent}>

You may be wondering about the background color line (i.e. var(--color, var(--off-white));). What this snippet means is that by default the background will be our --off-white value. But, if we set a --color custom property on a card, it will override and choose that value instead.

At first, we’ll want all our cards to be --off-white, but we may want to change the value for individual cards later. This works very similarly to props in React. We can set a default value but create a slot where we can choose other values in specific circumstances. So, I encourage us to think of CSS custom properties like CSS’s version of props.

The style still won’t look great because we want to make sure that the images stay within their containers. Next.js’s Image component with the layout="fill" prop gets position: absolute; from the framework, so we can limit the size by putting in a container with position: relative;.

Let’s add a new class to our TeaListItem.module.css:

.ImageContainer { position: relative; width: 100%; height: 10em; overflow: hidden;
}

And then let’s add className={styles.ImageContainer} on the <div> that contains our <Image>. I use relatively “simple” names such as ImageContainer because we’re inside a CSS module, so we don’t have to worry about conflicting with the outside style.

Finally, we want to add a bit of padding on the sides of the text, so let’s add one last class and rely on the spacing variables we set up as design tokens:

.Title { padding-left: var(--space-sm); padding-right: var(--space-sm);
}

We can add this class to the <div> that contains our name and description. Now, our cards don’t look so bad:

Combining Global And Local Style

Next, we want to have our cards show in a grid layout. In this case, we’re just at the border between local and global styles. We could certainly code our layout directly on the TeaList component. But, I could also imagine that having a utility class that turns a list into a grid layout could be useful in several other places.

Let’s take the global approach here and add a new utility class in our styles/utilities.css:

.grid { list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--min-item-width, 30ch), 1fr)); gap: var(--space-md);
}

Now, we can add the .grid class on any list, and we’ll get an automatically responsive grid layout. We can also change the --min-item-width custom property (by default 30ch) to change the minimum width of each element.

Note: Remember to think of custom properties like props! If this syntax looks unfamiliar, you can check out “Intrinsically Responsive CSS Grid With minmax() And min()” by Chris Coyier.

As we’ve written this style globally, it doesn’t require any fanciness to add className="grid" onto our TeaList component. But, let’s say we want to couple this global style with some additional local store. For example, we want to bring a bit more of the “tea aesthetic” in and to make every other card have a green background. All we’d need to do is make a new components/TeaList/TeaList.module.css file:

.TeaList > :nth-child(even) { --color: var(--green);
}

Remember how we made a --color custom property on our TeaListItem component? Well, now we can set it under specific circumstances. Note that we can still use child selectors within CSS modules, and it doesn’t matter that we’re selecting an element that is styled inside a different module. So, we can also use our local component styles to affect child components. This is a feature rather than a bug, as it allows us to take advantage of the CSS cascade! If we tried to replicate this effect some other way, we’d likely end up with some kind of JavaScript soup rather than three lines of CSS.

Then, how can we keep the global .grid class on our TeaList component while also adding the local .TeaList class? This is where the syntax can get a bit funky because we have to access our .TeaList class out of the CSS module by doing something like style.TeaList.

One option would be to use string interpolation to get something like:

<ul role="list" className={`${style.TeaList} grid`}>

In this small case, this might be good enough. If we’re mixing-and-matching more classes, I find that this syntax makes my brain explode a bit, so I will sometimes opt to use the classnames library. In this case, we end up with a more sensible-looking list:

<ul role="list" className={classnames(style.TeaList, "grid")}>

Now, we’ve finished up our Shop page, and we’ve made our TeaList component take advantage of both global and local styles.

A Balancing Act

We’ve now built our tea shop using only plain CSS to handle the styling. You may have noticed that we did not have to spend ages dealing with custom Webpack setups, installing external libraries, and so on. That’s because of the patterns that we’ve used work with Next.js out of the box. Furthermore, they encourage best CSS practices and fit naturally into the Next.js framework architecture.

Our CSS organization consisted of four key pieces:

  1. Design tokens,
  2. Global styles,
  3. Utility classes,
  4. Component styles.

As we continue building our site, our list of design tokens and utility classes will grow. Any styling that doesn’t make sense to add as a utility class, we can add into component styles using CSS modules. As a result, we can find a continuous balance between local and global styling concerns. We can also generate performant, intuitive CSS code that grows naturally alongside our Next.js site.

The Full-funnel Product Detail Page

Once found only at the bottom of a sales funnel, the ecommerce product detail page is evolving to address the entire buyer journey, from awareness to sale.

Consider a free, basic theme on Shopify called “Simple,” Its product pages are sales-focused, with eight sections:

  • Product name,
  • Price,
  • Description
  • Product options,
  • Images,
  • Two call-to-action buttons,
  • Social media buttons,
  • Product recommendations.

These sections are fundamental to conversions except, perhaps, the social media buttons and product recommendations. The rest — product name, price, description, images, and add-to-cart buttons — are more or less essential to completing the action.

Screenshot of Simple's product detail pageScreenshot of Simple's product detail page

The product detail page on Simple, a Shopify theme, provides bottom-of-funnel content to prompt a purchase.

Interestingly, this list of product content sections is similar to a typical Amazon product detail page years ago. A screen capture from the Internet Archive shows that the product page in 2007 for the “George Forman Grill with Bun Warmer” had the basics — name, price, description, picture, and add to cart button — plus ratings, tags, and a customer discussion section.

The page was aimed at the bottom of the funnel — the end of the buyer’s journey.

Screenshot from 2007 of George Forman Grill on Amazon.Screenshot from 2007 of George Forman Grill on Amazon.

Amazon’s product detail pages in 2007 contained content for end-of-journey actions.

Funnels and Journeys

Sales funnels and buyer’s journeys are meant to describe how prospects become customers. These models help us think about the required emotional, psychological, and physical steps.

Historically, such models have focused on the action steps. As an example, consider the popular AIDAR model, a hierarchical funnel with five stages:

  • Attention,
  • Interest,
  • Desire,
  • Action,
  • Retention.

A product detail page with just the basic info described above fits best near the bottom of the model in the “Action” or “Retention” step.

A diagram showing the five steps of the AIDAR model. A diagram showing the five steps of the AIDAR model.

The AIDAR model is an example of a marketing or sales funnel.

Full-funnel

Product detail pages in 2021 have evolved from closing the sale to serving the entire journey — a full-funnel page.

Take the product detail page on Amazon for Wow Apple Cider Vinegar Shampoo from July 22, 2021. On marketplaces such as Amazon, product pages have to attract attention in search, generate interest, and develop desire so that a shopper will make a purchase.

Screenshot of the Wow Apple Cider Vinegar Shampoo page on AmazonScreenshot of the Wow Apple Cider Vinegar Shampoo page on Amazon

The Wow Apple Cider Vinegar Shampoo page on Amazon has to attract attention in search, generate interest, and develop desire so that a shopper will make a purchase.

The product name is long.

“WOW Apple Cider Vinegar Shampoo – Reduce Dandruff, Frizz, Split Ends, For Hair Loss – Clean Scalp & Boost Gloss, Shine – Paraben, Sulfate Free – All Hair Types, Adults & Children – 500 mL”

This title is not likely aimed at the bottom of the funnel but rather near the top. WOW seems to be optimizing it for search on Amazon.

Moreover, the “About this item” section adds 200 more words explaining what the shampoo does. The description here may be aimed at the middle of the funnel to create a desire for the product.

Screenshot from Amazon of the extended description for Wow Apple Cider Vinegar ShampooScreenshot from Amazon of the extended description for Wow Apple Cider Vinegar Shampoo

On Amazon, the Wow Apple Cider Vinegar Shampoo page contains content aimed at consumers earlier in their shopping journey.

And the list goes on. The page includes three product videos and six product-related photos. There’s a 1,097-word product description (longer than most articles on Practical Ecommerce), two charts, two bulleted lists, and 20 more photos. And there are sections for specifications, ingredients, usage, reviews, and user videos.

The WOW shampoo product page had three videos, numerous photos, and more words than most articles on Practical Ecommerce.

Evolution

Several factors are likely responsible for the differences on the Amazon product detail pages for the George Forman grill in 2007 versus the WOW shampoo in 2021.

Competition. Worldwide retail ecommerce is booming, with sales nearly tripling in the six years from 2014 to 2020 when it hit $4.28 trillion, according to Statista. And B2B ecommerce is even bigger. Global B2B ecommerce revenue reached more than $6.6 trillion in 2020, according to Grand View Research.

Thus ecommerce is competitive. Product information can provide an edge.

In a discussion about search engine optimization and link building, James Wirth, senior director of strategy and growth marketing at Citation Labs, an agency, pointed out that his firm builds links to ecommerce product pages. That process requires link-worthy content.

Nowhere is ecommerce competition as intense as on Amazon, where a product page is not the culmination of a buyer’s journey but likely the first stop. That page is the only opportunity a brand or retailer has to grab attention, peak interest, and develop desire.

Covid-19. The pandemic intensified ecommerce competition, forced more businesses to compete online, and impacted buyer behavior.

In December 2020, Salsify, a maker of product information management software, surveyed 1,800 U.S. shoppers. About 40% of respondents had relied on ecommerce in some form since the outset of the pandemic. Roughly 24% only shopped online. And another 37% limited brick-and-mortar shopping to trusted stores.

Thus shoppers who might otherwise have gone to a local store were pursuing digital aisles. Those shoppers wanted product information, including top-of-funnel content.

Culture. Finally, shopping culture is changing.

Buyers are not just interested in price. They want to know if a product is good not just in quality, but also good for the world. Consumers increasing are interested in sustainability, social responsibility, and the well-being of workers.

This could be why shoppers might prefer a minority-owned company or why WOW’s shampoo description states that its products are vegan and cruelty-free. All of this needs to be communicated. Product pages might be the place to do it.