Better Fathers, Husbands, Leaders Drive ‘Order of Man’ Founder

In March 2015 Ryan Michler owned a successful financial planning company. His clients were medical professionals. He earned a comfortable living.

“I was making great money,” Michler told me. “But I felt like something was missing. I thought there were opportunities to help men. And that’s when I started.”

What he started was Order of Man, a website, podcast, Facebook group, and paid membership to help men become better financial stewards, better fathers and husbands, and better leaders.

I spoke recently with Michler on launching the business and growing the community, among other topics. What follows is our entire audio conversation and a transcript, edited for clarity and length.

Eric Bandholz: Tell us yourself.

Ryan Michler: I’m the founder of Order of Man, which is an organization centered around giving men the tools, conversations, frameworks, and resources to be more capable. We’re helping males step up in their families, their businesses, their communities — wherever it is they’re showing up. We launched in 2015.

Until recently, I was doing all roles, from podcast host to editor to website developer to social media guy. But over the past year, I’ve tried to bring in other people who are better equipped to deal with that stuff. It frees up my time to focus on the ideas and the vision and the direction — all the higher-level thinking. I don’t have employees, but I do have quite a few contractors who handle different tasks, such as podcast editing, social media content, YouTube videos, things like that.

And then I have a fairly sizable crew that helps me administer our digital membership called the Iron Council. So we have team leaders there — a network manager, an event coordinator. I’ve tried to outsource a lot of stuff. It’s been a good change. Challenging but good.

Bandholz: What’s driving revenue?

Michler: The primary revenue source is the Iron Council. We have over 500 members. I started that a little over four years ago with a single educational course. That course developed and turned into this digital fraternity. But then we have guys asking for merchandise or hats or shirts. The store now generates 10 to 15 percent of our revenue. Roughly 70 percent is from memberships to the Iron Council. And then we offer one-off courses and two to four live events per year.

Bandholz: What about sponsorships on your podcast?

Michler: Yes, I do that, but it’s not a material revenue generator. I don’t focus on it. I’d much rather share what we have available internally.

Bandholz: Do you advertise your podcast to drive listeners?

Michler: I’ve never seriously advertised. The most I’ve ever done is boosting Facebook and Instagram posts. Our largest social media channel is our closed Facebook group with roughly 68,000 members. So between the podcast and that group, we generate a lot of excitement and awareness about what we’re doing.

Bandholz: Is your Facebook group free?

Michler: Yes. It’s free, exclusively for men. And all they have to do is request access. We have a few minor barriers to make sure that these are guys who are motivated to contribute in a meaningful way. And then we’d have a moderator team that keeps all participants focused.

Bandholz: Talk about what it takes to build a successful Facebook group.

Michler: The difficulty wasn’t in the early days, surprisingly. When it was 500 guys or a thousand guys or even 5,000 guys, it was much easier to manage than now. There are a lot of negative attitudes. The discussions quite often get out of hand. There’s some disrespect and crude and rash behavior. We get rid of those people. But if members have a healthy, respectful disagreement, by all means let’s do it.

We don’t allow posting of memes in the group. We minimize the self-promotional stuff. You can’t share links to your own sites and programs. Again, we want respectful, meaningful conversations.

Bandholz: Let’s go back to the early days, five years ago. Was it a side hustle for you then?

Michler: It was. I started in March 2015. I owned a financial planning practice then. I was making great money, but I felt like something was missing. So I started a podcast for that financial planning practice. It was called “Wealth Anatomy.” It was geared towards providing financial advice for medical professionals. And I realized I love podcasting.

I thought there were opportunities here to help men. And that’s when I started. I went about seven months or so before I made my first dollar. Initially, I was going to have ads on the website and the podcast. But I realized quickly that I didn’t have the audience. And I’m glad it worked out that way. At roughly seven months, my wife and I had a conversation. She said, “I appreciate that you’re doing this Order of Man thing, but you’re not making any money, and it’s detracting from your practice, which is taking away from household income. So you should probably scale back or maybe find a way to make a little money doing it.”

And I told her something like, “Well, I’m not scaling back. If anything, I’m doubling down on this thing.” I had listened to a podcast, which suggested making a course. I’m like, “Perfect. I’ll do a course.” I released it in roughly October or November of 2015. It was called the Iron Council. It was available for 12 men. That’s it. And over 12 weeks we were going to discuss six topics. We had a weekly telephone call, an assignment per topic, and a private Facebook group — all focused on the Iron Council membership.

I charged $100 for it and sold out overnight. I didn’t know what to do next. So that first month, October, November, we made $1,200. And then two-thirds of the way through, the guys are asking, “What are we doing after this is over?” I hadn’t even thought about it. I still had my income from the financial planning practice. But I decided to open up the Iron Council and turn it more into a membership. And we ramped that up to about a hundred members, fairly quickly, when we opened it up at the beginning of 2016.

And it has been on fire ever since. I ended up selling my financial planning practice. This is all I’ve done for the last three years or so.

Bandholz: The podcast and the group have been a big catalyst for building your community. You also wrote a book, “Sovereignty: The Battle for the Hearts and Minds of Men.” Can you talk about that?

Michler: I wrote the book as another medium to get my message out to people who wouldn’t hear it via the podcast or be in the Facebook group. It was access to a whole new group of men. People who read books are motivated, ambitious, and want to learn and grow. But the thing that I didn’t realize in writing the book is that it helped me crystallize and solidify my message.

It also gave me an added level of credibility with my community.

Bandholz: Where can people learn more about you?

Michler: Our website is Orderofman.com. Our podcast, too, is called “Order of Man.” Listeners can connect with me on Twitter (@ryanmichler) or Instagram (@ryanmichler).

Building Reusable React Components Using Tailwind

Building Reusable React Components Using Tailwind

Building Reusable React Components Using Tailwind

Tilo Mitra

2020-05-25T11:00:00+00:00 2020-05-26T01:09:55+00:00

In this post, we’ll look at several different ways you can build reusable React components that leverage Tailwind under the hood while exposing a nice interface to other components. This will improve your code by moving from long lists of class names to semantic props that are easier to read and maintain.

You will need to have worked with React in order to get a good understanding of this post.

Tailwind is a very popular CSS framework that provides low-level utility classes to help developers build custom designs. It’s grown in popularity over the last few years because it solves two problems really well:

  1. Tailwind makes it easy to make iterative changes to HTML without digging through stylesheets to find matching CSS selectors.
  2. Tailwind has sane conventions and defaults. This makes it easy for people to get started without writing CSS from scratch.

Add the comprehensive documentation and it’s no surprise why Tailwind is so popular.

These methods will help you transform code that looks like this:

<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable
</button>

To code that looks like this:

<Button size="sm" textColor="white" bgColor="blue-500"> Enable
</Button>

The difference between both snippets is that in the first we made use of a standard HTML button tag, while the second used a <Button> component. The <Button> component had been built for reusability and is easier to read since it has better semantics. Instead of a long list of class names, it uses properties to set various attributes such as size, textColor, and bgColor.

Let’s get started.

Method 1: Controlling Classes With The Classnames Module

A simple way to adapt Tailwind into a React application is to embrace the class names and toggle them programmatically.

The classnames npm module makes it easy to toggle classes in React. To demonstrate how you may use this, let’s take a use case where you have <Button> components in your React application.

// This could be hard to read.
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button> // This is more conventional React.
<Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

Let’s see how to separate Tailwind classes so people using this <Button> component can use React props such as size, textColor, and bgColor.

  1. Pass props such as bgColor and textColor directly into the class name string template.
  2. Use objects to programmatically switch class names (as we have done with the size prop)

In the example code below, we’ll take a look at both approaches.

// Button.jsx
import classnames from 'classnames'; function Button ({size, bgColor, textColor, children}) { return ( <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", { "text-xs": size === 'sm' "text-xl": size === 'lg', })}> {children} </button> )
}; export default Button;

In the code above, we define a Button component that takes the following props:

  • size
    Defines the size of the button and applies the Tailwind classes text-xs or text-xl
  • bgColor
    Defines the background color of the button and applies the Tailwind bg-* classes.
  • textColor
    Defines the text color of the button and applies the Tailwind text-* classes.
  • children
    Any subcomponents will be passed through here. It will usually contain the text within the <Button>.

By defining Button.jsx, we can now import it in and use React props instead of class names. This makes our code easier to read and reuse.

import Button from './Button';
<Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>

Using Class Names For Interactive Components

A Button is a very simple use-case. What about something more complicated? Well, you can take this further to make interactive components.

For example, let’s look at a dropdown that is made using Tailwind.



An interactive dropdown built using Tailwind and class name toggling.

For this example, we create the HTML component using Tailwind CSS classnames but we expose a React component that looks like this:

<Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />

Looking at the code above, you’ll notice that we don’t have any Tailwind classes. They are all hidden inside the implementation code of <Dropdown/>. The user of this Dropdown component just has to provide a list of options and a click handler, onOptionSelect when an option is clicked.

Let’s see how this component can be built using Tailwind.

Removing some of the unrelated code, here’s the crux of the logic. You can view this Codepen for a complete example.

import classNames from 'classnames'; function Dropdown({ options, onOptionSelect }) { // Keep track of whether the dropdown is open or not. const [isActive, setActive] = useState(false); const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`; return ( // Toggle the dropdown if the button is clicked <button onClick={() => setActive(!isActive)} className={buttonClasses}> Options </button> // Use the classnames module to toggle the Tailwind .block and .hidden classes <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", { block: isActive, hidden: !isActive })}> // List items are rendered here. {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)} </div> )
} export default Dropdown;

The dropdown is made interactive by selectively showing or hiding it using the .hidden and .block classes. Whenever the <button> is pressed, we fire the onClick handler that toggles the isActive state. If the button is active (isActive === true), we set the block class. Otherwise, we set the hidden class. These are both Tailwind classes for toggling display behavior.

In summary, the classnames module is a simple and effective way to programmatically control class names for Tailwind. It makes it easier to separate logic into React props, which makes your components easier to reuse. It works for simple and interactive components.

Method 2: Using Constants To Define A Design System

Another way of using Tailwind and React together is by using constants and mapping props to a specific constant. This is effective for building design systems. Let’s demonstrate with an example.

Start with a theme.js file where you list out your design system.

// theme.js (you can call it whatever you want)
export const ButtonType = { primary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", secondary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", basic: "bg-white hover:bg-gray-700 text-gray-700 font-bold rounded", delete: "bg-red-300 hover:bg-red-500 text-white font-bold rounded"
}; export const ButtonSize = { sm: "py-2 px-4 text-xs", lg: "py-3 px-6 text-lg"
}

In this case, we have two sets of constants:

  • ButtonType defines how buttons are styled in our app.
  • ButtonSizes defines the sizes of buttons in our app.

Now, let’s write our <Button> component:

import {ButtonType, ButtonSize} from './theme'; function Button({size, type, children}) { // This can be improved. I’m keeping it simple here by joining two strings. const classNames = ButtonType[type] + " " + ButtonSize[size]; return ( <button className={classNames}>{children}</button> )
}
export default Button;

We use the ButtonType and ButtonSize constants to create a list of class names. This makes the interface of our <Button> much nicer. It lets us use size and type props instead of putting everything in a class name string.

// Cleaner and well defined props.
<Button size="xs" type="primary">Enable</Button>

Versus the prior approach:

// Exposing class names
<button className="py-2 px-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Enable</button>

If you need to redefine how buttons look in your application, just edit the theme.js file and all buttons in your app will automatically update. This can be easier than searching for class names in various components.

Method 3: Composing Utilities With @apply

A third way to improve the legibility of your React components is using CSS and the @apply pattern available in PostCSS to extract repeated classes. This pattern involves using stylesheets and post-processors.

Let’s demonstrate how this works through an example. Suppose you have a Button group that has a Primary and a Secondary Button.

A Button Group consisting of a primary and secondary button
A Button Group consisting of a primary and secondary button. (Large preview)
<button className="py-2 px-4 mr-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Update Now</button> <button className="py-2 px-4 text-xs mr-4 hover:bg-gray-100 text-gray-700 border-gray-300 border font-bold rounded">Later</button>

Using the @apply pattern, you can write this HTML as:

<button className="btn btn-primary btn-xs">Update Now</button>
<button className="btn btn-secondary btn-xs">Later</button>

Which can then be adopted to React to become:

import classnames from "classnames"; function Button ({size, type, children}) { const bSize = "btn-" + size; const bType = "btn-" + type; return ( <button className={classnames("btn", bSize, bType)}>{children}</button> )
} Button.propTypes = { size: PropTypes.oneOf(['xs, xl']), type: PropTypes.oneOf(['primary', 'secondary'])
}; // Using the Button component.
<Button type="primary" size="xs">Update Now</Button>
<Button type="secondary" size="xs">Later</Button>

Here’s how you would create these BEM-style classnames such as .btn, .btn-primary, and others. Start by creating a button.css file:

/\* button.css \*/ @tailwind base;
@tailwind components; .btn { @apply py-2 px-4 mr-4 font-bold rounded;
}
.btn-primary { @apply bg-blue-500 hover:bg-blue-700 text-white;
}
.btn-secondary { @apply hover:bg-gray-700 text-gray-700 border-gray-300 border;
}
.btn-xs { @apply text-xs;
}
.btn-xl { @apply text-xl;
} @tailwind utilities;

The code above isn’t real CSS but it will get compiled by PostCSS. There’s a GitHub repository available here which shows how to setup PostCSS and Tailwind for a JavaScript project.

There’s also a short video that demonstrates how to set it up here.

Disadvantages Of Using @apply

The concept of extracting Tailwind utility classes into higher-level CSS classes seems like it makes sense, but it has some disadvantages which you should be aware of. Let’s highlight these with another example.

First, by extracting these class names out, we lose some information. For example, we need to be aware that .btn-primary has to be added to a component that already has .btn applied to it. Also, .btn-primary and .btn-secondary can’t be applied together. This information is not evident by just looking at the classes.

If this component was something more complicated, you would also need to understand the parent-child relationship between the classes. In a way, this is the problem that Tailwind was designed to solve, and by using @apply, we are bringing the problems back, in a different way.

Here’s a video where Adam Wathan — the creator of Tailwind — dives into the pros and cons of using @apply.

Summary

In this article, we looked at three ways that you can integrate Tailwind into a React application to build reusable components. These methods help you to build React components that have a cleaner interface using props.

  1. Use the classnames module to programmatically toggle classes.
  2. Define a constants file where you define a list of classes per component state.
  3. Use @apply to extract higher-level CSS classes.

If you have any questions, send me a message on Twitter at @tilomitra.

Recommended Reading on SmashingMag:

Smashing Editorial (ks, ra, il)

Using Filters in Google Analytics

Accurate data is essential for decision-making. Google Analytics can provide that data, but it may need a bit of sanitizing. Filters in Google Analytics can remove data impurities, such as:

  • Development or other internal (employee) traffic,
  • Inconsistent capitalization of URLs, titles, events, campaigns, and similar,
  • Multiple websites or hostnames are showing up in a single Analytics property.

In this post, I’ll introduce Google Analytics filters.

Creating a Filter

Access filters in the Admin tab of Google Analytics at the “Account” level or “View” level. (A “View” is a subset of an account “Property” that can have its own configuration settings for specific users or reporting.)

Access filters in the Admin tab at the "Account" or "View" level.

Access filters in the Admin tab at the “Account” or “View” level. Click image to enlarge.

Admin> Account > All Filters lists all filters for all Views. You can add, remove, or create new filters across all Views.

At Admin > View > Filters, you can create a filter or add an existing filter. I’ll use the View settings for the examples in this post.

Google Analytics does not include default filters. Thus no filters will appear unless you add them.

Google Analytics has no default filters. No filters will appear unless you add them, such as these examples.

Google Analytics has no default filters. No filters will appear unless you add them, such as these examples. Click image to enlarge.

IP exclude filters exclude specific IP addresses or ranges, typically to block the reporting of employee traffic. Use descriptive names for this filter (such as “Exclude Office Traffic” or “Exclude My Home IP Address”), instead of repeating the IP address as the name.

Use descriptive names for IP filter — such as “Exclude Office Traffic” or “Exclude My Home IP Address" — instead of repeating the IP address.

Use descriptive names for IP filter — such as “Exclude Office Traffic” or “Exclude My Home IP Address” — instead of repeating the IP address.

“Include” filters allow in reports only traffic from specific hostnames, geographic locations, campaigns, and other traffic.

"Include" filters allow the reporting only from specific hostnames, geographic locations, campaigns, and other traffic.

“Include” filters allow the reporting only from specific hostnames, geographic locations, campaigns, and other traffic.

Uppercase and lowercase filters clean up your inconsistent case in Events, Pages, Campaigns, and more. The lowercase filter is more common than uppercase.

Uppercase and lowercase filters clean up the inconsistent case in Events, Pages, Campaigns, and more.

Uppercase and lowercase filters clean up the inconsistent case in Events, Pages, Campaigns, and more.

Search and replace filters and advanced filters modify field values. A typical example appends the hostname to the request URI (i.e., the web page), which can be helpful when a business has multiple websites and subdomains that visitors access in a single session.

A common advanced filter appends the hostname ("hub.powermyanalytics.com") to a web page.

A common advanced filter appends the hostname (“hub.powermyanalytics.com”) to a web page.

The settings for an advanced filter are:

  • Filter Type. Advanced
  • Field A. Extract A: Hostname = (.*) The (.*) characters capture everything.
  • Field B. Extract B: Request URI = (.*)
  • Output To. Constructor: Request URI = $A1$B1. This means the two fields will be combined.

Make sure the boxes “Field A Required” and “Override Output Field” are checked and leave the others unchecked. This means the hostname must be present and that the Request URI field will be overwritten with the combined hostname and Request URI.

Make sure the boxes “Field A Required” and “Override Output Field” are checked and leave the others unchecked.

Make sure the boxes “Field A Required” and “Override Output Field” are checked and leave the others unchecked.

Verify Filters

Check your filters as soon as possible to ensure they work as intended, to minimize the impact on reporting if something went wrong. There are several ways to do this.

In the filter itself. Some filters allow you to view how the data will change the reporting.

Some filters will provide a preview of how the data will change the reporting.

Some filters will provide a preview of how the data will change the reporting.

Testing the same settings in your reports. If you include only a specific hostname, you can apply this same filter to your hostname report at Audience > Technology > Network, then click on the “Hostname” tab between the graph and table on the page.

When verifying, align the filter settings with the setup at the View level.

Align the filter settings when verifying with the setup at the View level.

Align the filter settings when verifying with the setup at the View level. Click image to enlarge.

For example, if you used “that are equal to” in the filter settings at the View level, use that same setting when verifying in your reports.

If you used “that are equal to” at the View level, use that same setting when verifying in your reports.

If you used “that are equal to” at the View level, use that same setting when verifying in your reports.

Realtime reports. Confirm in Realtime reports that the filters are working correctly. Certain filters, such as “Lowercase for Events,” are easier to confirm than others. If you see incorrect Events showing up in Realtime, revisit your filter.

The Realtime report in this example shows that the filters are not working.

The Realtime report in this example shows that the filters are not working.

Subsequent reports, such as later in the day or the next day.

Filter Order

The order that filters are listed for a View impacts the data. In general, place filters in this order:

  1. Include/exclude filters.
  2. Search and replace filters or advanced filters.
  3. Lowercase/uppercase filters.

The sequence above simplifies the process and enables the filters to apply to as much raw data as possible. For example, there is no reason to lowercase a hostname that you will filter out anyway.

You can modify the filter order by clicking the “Assign Filter Order” button.

Modify the filter order by clicking the “Assign Filter Order” button.

Modify the filter order by clicking the “Assign Filter Order” button.

For more resources, see “About View filters” in Google’s “Analytics Help.”