Monday, 25 December 2017

Mozzy Good Wishes to You & Yours!

Posted by FeliciaCrawford

As the long holiday weekend draws to a close and we prepare to welcome a brand-new year, we at Moz just want to thank you all for a wonderful, fulfilling year on the blog. Your colorful commentary, delightful debates, thrilling thumbs-up, and vivacious visits have made the past twelve months sparkle and shine (and with that, I'll bid the alliteration adieu).

Our "card" features a cameo from a little Moz Dog you may recognize: the inimitable Lettie Pickles!

At the Moz HQ, we practice a multitude of holiday traditions. Whether it's Mozzers gathering in the common room (affectionately named "Roger") to light candles on the menorah during Hanukkah, trading and stealing gifts for the company-wide White Elephant exchange (someone won a bonafide Commodore 64 this year!), or getting our boogie and our board gaming on at the Moz holiday party, we try to honor this special season with a healthy mix of reverence and good old-fashioned fun.

The folks who come to our blog for digital marketing advice hail from almost every remote corner of the world (we know; we looked at our analytics ;). This week, when things tend to slow down and it's just a little more difficult than usual to get anyone to reply to your emails, we'd love to invite you to share your own unique tales and traditions in the comments. What's your favorite way to celebrate, in the office and at home? What mishaps and magical moments alike filled your days, and what's your resolution for 2018? Let's take a little breather as we gear up for all the new projects and responsibilities awaiting us just around the corner and share with each other; after all, that's what being a community is all about! :)


Sign up for The Moz Top 10, a semimonthly mailer updating you on the top ten hottest pieces of SEO news, tips, and rad links uncovered by the Moz team. Think of it as your exclusive digest of stuff you don't have time to hunt down but want to read!



from The Moz Blog http://ift.tt/2DMCBes
via IFTTT

2017 Staff Favorites

Sunday, 24 December 2017

Invision Studio

Studio is the name of the new design tool by the team at InVision that’ll launch in January 2018 and it looks like it has a lot of great features, with shared component libraries being one of the more interesting features that I can’t wait to take a closer look at. Also I’m sure that it’ll integrate really nicely with InVision’s existing tools and apps to make prototyping a whole lot easier.

Direct Link to ArticlePermalink


Invision Studio is a post from CSS-Tricks



from CSS-Tricks http://ift.tt/2ywBZcv
via IFTTT

Saturday, 23 December 2017

Many Ways to Learn

Julie Zhuo responds to the classic "What can I do to continue my growth?":

One of the things I believe the most firmly is that everyone has something to teach you if you’re looking for the lessons. And these people don’t have to be other designers at your company! There are many paths to becoming an awesome product designer

She lists (and explains):

  • Learn from your users
  • Learn from people with different skillsets
  • Learn by doing 

I have a draft blog post called "Tech Books are Supplementary" that I started in 2011 and somehow haven't gotten around to finishing. One of these days! The point I try to make in it, as you can imagine, is that tech books are just a slice of the learning pie.

I'm playing a lot more banjo lately, trying to level up the best I can. You know what it takes? Going to jams. YouTubing people playing the songs I want to learn. Asking for advice. Listening to tons of recordings. Playing along to those recordings. Buying and reading books on the topic. Finding tabs online.

Learning things well takes hitting it from all sides.

Direct Link to ArticlePermalink


Many Ways to Learn is a post from CSS-Tricks



from CSS-Tricks http://ift.tt/2C9wyiG
via IFTTT

Friday, 22 December 2017

Chrome is Not the Standard

Chris Krycho has written an excellent post about how us fickle web developers might sometimes confuse features that land in one browser as being “the future of the web.” However, Chris argues that there’s more than one browser’s vision of the web that we should care about:

No single company gets to dominate the others in terms of setting the agenda for the web. Not Firefox, with its development and advocacy of WebAssembly, dear to my heart though that is. Not Microsoft and the IE/Edge team, with its proposal of the CSS grid spec in 2011, sad though I am that it languished for as long as it did. Not Apple, with its pitch for concurrent JavaScript. And not—however good its developer relations team is—Chrome, with any of the many ideas it’s constantly trying out, including PWAs.

It’s also worth recognizing how these decisions aren’t, in almost any case, unalloyed pushes for “the future of the web.” They reflect business priorities, just like any other technical prioritization.

I particularly like Chris’ last point about business priorities because I think it’s quite easy to forget that browser manufacturers aren’t making the web a better place out of sheer kindness; they’re companies with investors and incentives that might not always align with other companies’ objectives.

Direct Link to ArticlePermalink


Chrome is Not the Standard is a post from CSS-Tricks



from CSS-Tricks http://ift.tt/2DnvVDx
via IFTTT

The Rise of the Butt-less Website

It seems like all the cool kids have divided themselves into two cliques: the Headless CMS crowd on one side and the Static Site Generator crowd on the other. While I admit those are pretty cool team names, I found myself unable to pick a side. To paraphrase Groucho Marx, “I don't care to belong to any club that will have me as a member.”

For my own simple blog (which is embarrassingly empty at the moment), a static site generator could be a great fit. Systems like Hugo and Jekyll have both been highly recommended by developers I love and trust and look great at first glance, but I hit stumbling blocks when I wanted to change my theme or set up more complex JavaScript and interactions across pages. There are ways to solve both these issues, but that’s not the kind of weekend I want to have.

Besides that, I love to experiment, make new things, and I’ve got a major crush on Vue at the moment. Having a Headless CMS setup with a front-end that is decoupled from the back-end could be a great combination for me, but after 7+ years of PHP slinging with WordPress, the whole setup feels like overkill for my basic needs.

What I really want is a static site generator that will let me write a blog as a component of a larger single-page app so I have room to try new things and still have full control over styling, without the need for a database or any sort of back-end. This is a long way of telling you that I’ve found my own club to join, with a decidedly un-cool name.

Get ready for it...

The Butt-less Website

Because there’s no back-end, get it? 😶

It takes a few steps to go butt-less:

  1. Setup a single page app with Vue
  2. Generate each route at build time
  3. Create blog and article components
  4. Integrate Webpack to parse Markdown content
  5. Extend functionality with plugins
  6. Profit!

That last point has to be a part of every proposal, right?

I know it looks like a lot of steps but this is not quite as tough as it seems. Let's break down the steps together.

Setup a single page app with Vue

Let's get Vue up and running. We're going to need Webpack to do that.

I get it, Webpack is pretty intimidating even when you know what’s going on. It’s probably best to let someone else do the really hard work, so we’ll use the Vue Progressive Web App Boilerplate as our foundation and make a few tweaks.

We could use the default setup from the repo, but even while I was writing this article, there were changes being made there. In the interest of not having this all break on us, we will use a repo I created for demonstration purposes. The repo has a branch for each step we'll be covering in this post to help follow along.

View on GitHub

Cloning the repo and check out the step-1 branch:

$ git clone http://ift.tt/2pdbw0J step-1
$ cd vue-yes-blog
$ npm install
$ npm run dev

One of my favorite parts of modern development is that it takes a mere thirty seconds to get a progressive web app up and running!

Next, let’s complicate things.

Generate each route at build time

Out of the box, single page apps only have a single entry point. In other words, it lives lives at a single URL. This makes sense in some cases, but we want our app to feel like a normal website.

We’ll need to make use of the history mode in the Vue Router file in order to do that. First, we’ll turn that on by adding mode: 'history' to the Router object’s properties like so:

// src/router/index.js
Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
// ...

Our starter app has two routes. In addition to Hello, we have a second view component called Banana that lives at the route /banana. Without history mode, the URL for that page would be http://localhost:1982/#/banana. History mode cleans that up to http://localhost:1982/banana. Much more elegant!

All this works pretty well in development mode (npm run dev), but let’s take a peek at what it would look like in production. Here's how we compile everything:

$ npm run build

That command will generate your Vue site into the ./dist folder. To see it live, there’s a handy command for starting up a super simple HTTP server on your Mac:

$ cd dist
$ python -m SimpleHTTPServer

Sorry Windows folks, I don’t know the equivalent!

Now visit localhost:8000 in your browser. You’ll see your site as it will appear in a production environment. Click on the Banana link, and all is well.

Refresh the page. Oops! This reveals our first problem with single page apps: there is only one HTML file being generated at build time, so there’s no way for the browser to know that /banana should target the main app page and load the route without fancy Apache-style redirects!

Of course, there's an app for that. Or, at least a plugin. The basic usage is noted in the Vue Progressive Web App Boilerplate documentation. Here's how it says we can spin up the plugin:

$ npm install -D prerender-spa-plugin

Let's add our routes to the Webpack production configuration file:

// ./build/webpack.prod.conf.js
// ...
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin')
const PrerenderSpaPlugin = require('prerender-spa-plugin')
const loadMinified = require('./load-minified')
// ...
const webpackConfig = merge(baseWebpackConfig, {
  // ...
  plugins: [
  // ...
    new SWPrecacheWebpackPlugin({
      // ...
      minify: true,
      stripPrefix: 'dist/'
    }),
    // prerender app
    new PrerenderSpaPlugin(
      // Path to compiled app
      path.join(__dirname, '../dist'),
      // List of endpoints you wish to prerender
      [ '/', '/banana' ]
    )
  ]
})

That’s it. Now, when you run a new build, each route in that array will be rendered as a new entry point to the app. Congratulations, we’ve basically just enabled static site generation!

Create blog and article components

If you’re skipping ahead, we’re now up to the step-2 branch of my demo repo. Go ahead and check it out:

$ git checkout step-2

This step is pretty straightforward. We’ll create two new components, and link them together.

Blog Component

Let's register the the blog component. We'll create a new file called YesBlog.vue in the /src/components directory and drop in the markup for the view:

// ./src/components/YesBlog.vue
<template>
  <div class="blog">
    <h1>Blog</h1>
    <router-link to="/">Home</router-link>
    <hr/>
    <article v-for="article in articles" :key="article.slug" class="article">
      <router-link class="article__link" :to="`/blog/${ article.slug }`">
        <h2 class="article__title"></h2>
        <p class="article__description"></p>
      </router-link>
    </article>
  </div>
</template>

<script>
export default {
  name: 'blog',
  computed: {
    articles() {
      return [
        {
          slug: 'first-article',
          title: 'Article One',
          description: 'This is article one\'s description',
        },
        {
          slug: 'second-article',
          title: 'Article Two',
          description: 'This is article two\'s description',
        },
      ];
    },
  },
};
</script>

All we’re really doing here is creating a placeholder array (articles) that will be filled with article objects. This array creates our article list and uses the slug parameter as the post ID. The title and description parameters fill out the post details. For now, it’s all hard-coded while we get the rest of our code in place.

Article Component

The article component is a similar process. We'll create a new file called YesArticle.vue and establish the markup for the view:

// ./src/components/YesArticle.vue
<template>
  <div class="article">
    <h1 class="blog__title"></h1>
    <router-link to="/blog">Back</router-link>
    <hr/>
    <div class="article__body" v-html="article.body"></div>
  </div>
</template>

<script>
  export default {
    name: 'YesArticle',
    props: {
      id: {
        type: String,
        required: true,
      },
    },
    data() {
      return {
        article: {
          title: this.id,
          body: '<h2>Testing</h2><p>Ok, let\'s do more now!</p>',
        },
      };
    },
  };
</script>

We’ll use the props passed along by the router to know what article ID we’re working with. For now, we’ll just use that as the post title, and hardcode the body.

Routing

We can't move ahead until we add our new views to the router. This will ensure that our URLs are valid and allows our navigation to function properly. Here is the entirety of the router file:

// ./src/router/index.js
import Router from 'vue-router';
import Hello from '@/components/Hello';
import Banana from '@/components/Banana';
import YesBlog from '@/components/YesBlog';
import YesArticle from '@/components/YesArticle';

Vue.use(Router);

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'Hello',
      component: Hello,
    },
    {
      path: '/banana',
      name: 'Banana',
      component: Banana,
    },
    {
      path: '/blog',
      name: 'YesBlog',
      component: YesBlog,
    },
    {
      path: '/blog/:id',
      name: 'YesArticle',
      props: true,
      component: YesArticle,
    },
  ],
});

Notice that we've appended /:id to the YesArtcle component's path and set its props to true. These are crucial because they establish the dynamic routing we set up in the component's props array in the component file.

Finally, we can add a link to our homepage that points to the blog. This is what we drop into the Hello.vue file to get that going:

<router-link to="/blog">Blog</router-link>

Pre-rendering

We've done a lot of work so far but none of it will stick until we pre-render our routes. Pre-rendering is a fancy way of saying that we tell the app what routes exist and to dump the right markup into the right route. We added a Webpack plugin for this earlier, so here's what we can add to our Webpack production configuration file:

// ./build/webpack.prod.conf.js
// ...
  // List of endpoints you wish to prerender
  [ '/', '/banana', '/blog', '/blog/first-article', '/blog/second-article' ]
// ...

I have to admit, this process can be cumbersome and annoying. I mean, who wants to touch multiple files to create a URL?! Thankfully, we can automate this, which we'll cover further down.

Integrate Webpack to parse Markdown content

We’re now up to the step-3 branch. Check it out if you're following along in the code:

$ git checkout step-3

The Posts

We’ll be using Markdown to write our posts, with some FrontMatter to create meta data functionality.

Fire up a new file in the posts directory to create our very first post:

// ./src/posts/first-article.md
---
title: Article One from MD
description: In which the hero starts fresh
created: 2017-10-01T08:01:50+02
updated:
status: publish
---
Here is the text of the article.  It's pretty great, isn't it?



// ./src/posts/second-article.md
---
title: Article Two from MD
description: This is another article
created: 2017-10-01T08:01:50+02
updated:
status: publish
---
## Let's start with an H2
And then some text
And then some code:
```html
<div class="container">
  <div class="main">
    <div class="article insert-wp-tags-here">
      <h1>Title</h1>
      <div class="article-content">
        <p class="intro">Intro Text</p>
        <p></p>
      </div>
      <div class="article-meta"></div>
    </div>
  </div>
</div>
```

Dynamic Routing

One annoying thing at the moment is that we need to hardcode our routes for the pre-rendering plugin. Luckily, it isn’t complicated to make this dynamic with a bit of Node magic. First, we’ll create a helper in our utility file to find the files:

// ./build/utils.js
// ...
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const fs = require('fs')

exports.filesToRoutes = function (directory, extension, routePrefix = '') {
  function findFilesInDir(startPath, filter){
    let results = []
    if (!fs.existsSync(startPath)) {
      console.log("no dir ", startPath)
      return
    }
    const files = fs.readdirSync(startPath)
    for (let i = 0; i < files.length; i++) {
      const filename = path.join(startPath, files[i])
      const stat = fs.lstatSync(filename)
      if (stat.isDirectory()) {
        results = results.concat(findFilesInDir(filename, filter)) //recurse
      } else if (filename.indexOf(filter) >= 0) {
        results.push(filename)
      }
    }
    return results
  }

  return findFilesInDir(path.join(__dirname, directory), extension)
    .map((filename) => {
      return filename
        .replace(path.join(__dirname, directory), routePrefix)
        .replace(extension, '')
      })
}

exports.assetsPath = function (_path) {
// ...

This can really just be copied and pasted, but what we’ve done here is create a utility method called filesToRoutes() which will take in a directory, extension, and an optional routePrefix, and return an array of routes based on a recursive file search within that directory.

All we have to do to make our blog post routes dynamic is merge this new array into our PrerenderSpaPlugin routes. The power of ES6 makes this really simple:

// ./build/webpack.prod.conf.js
// ...
new PrerenderSpaPlugin(
  // Path to compiled app
  path.join(__dirname, '../dist'),
  // List of endpoints you wish to prerender
  [
    '/',
    '/banana',
    '/blog',
    ...utils.filesToRoutes('../src/posts', '.md', '/blog')
  ]
)

Since we've already imported utils at the top of the file for other purposes, we can just use the spread operator ... to merge the new dynamic routes array into this one, and we’re done. Now our pre-rendering is completely dynamic, only dependent on us adding a new file!

Webpack Loaders

We’re now up to the step-4 branch:

$ git checkout step-4

In order to actually turn our Markdown files into parse-able content, we’ll need some Webpack loaders in place. Again, someone else has done all the work for us, so we only have to install and add them to our config.

$ npm install -D json-loader markdown-it-front-matter-loader markdown-it highlight.js yaml-front-matter

We will only be calling the json-loader and markdown-it-front-matter-loader from our Webpack config, but the latter has peer dependencies of markdown-it and highlight.js, so we’ll install those at the same time. Also, nothing warns us about this, but yaml-front-matter is also required, so the command above adds that as well.

To use these fancy new loaders, we’re going to add a block to our Webpack base config:

// ./build/webpack.base.conf.js
// ...
module.exports = {
  // ...
  module: {
    rules: [
  // ...
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.md$/,
        loaders: ['json-loader', 'markdown-it-front-matter-loader'],
      },
    ]
  }
}

Now, any time Webpack encounters a require statement with a .md extension, it will use the front-matter-loader (which will correctly parse the metadata block from our articles as well as the code blocks), and take the output JSON and run it through the json-loader. This way, we know we’re ending up with an object for each article that looks like this:

// first-article.md [Object]
{
  body: "<p>Here is the text of the article. It's pretty great, isn't it?</p>\n"
  created: "2017-10-01T06:01:50.000Z"
  description: "In which the hero starts fresh"
  raw: "\n\nHere is the text of the article. It's pretty great, isn't it?\n"
  slug: "first-article"
  status: "publish"
  title: "Article One from MD"
  updated: null
}

This is exactly what we need and it’s pretty easy to extend with other metadata if you need to. But so far, this doesn’t do anything! We need to require these in one of our components so that Webpack can find and load it.

We could just write:

require('../posts/first-article.md')

...but then we’d have to do that for every article we create, and that won’t be any fun as our blog grows. We need a way to dynamically require all our Markdown files.

Dynamic Requiring

Luckily, Webpack does this! It wasn’t easy to find documentation for this but here it is. There is a method called require.context() that we can use to do just what we need. We’ll add it to the script section of our YesBlog component:

// ./src/components/YesBlog.vue
// ...
<script>
  const posts = {};
  const req = require.context('../posts/', false, /\.md$/);
  req.keys().forEach((key) => {
    posts[key] = req(key);
  });

  export default {
    name: 'blog',
    computed: {
      articles() {
        const articleArray = [];
        Object.keys(posts).forEach((key) => {
          const article = posts[key];
          article.slug = key.replace('./', '').replace('.md', '');
          articleArray.push(article);
        });
        return articleArray;
      },
    },
  };
</script>
// ...

What’s happening here? We’re creating a posts object that we’ll first populate with articles, then use later within the component. Since we’re pre-rendering all our content, this object will be instantly available.

The require.context() method accepts three arguments.

  • the directory where it will search
  • whether or not to include subdirectories
  • a regex filter to return files

In our case, we only want Markdown files in the posts directory, so:

require.context('../posts/', false, /\.md$/);

This will give us a kind of strange new function/object that we need to parse in order to use. That's where req.keys() will give us an array of the relative paths to each file. If we call req(key), this will return the article object we want, so we can assign that value to a matching key in our posts object.

Finally, in the computed articles() method, we’ll auto-generate our slug by adding a slug key to each post, with a value of the file name without a path or extensions. If we wanted to, this could be altered to allow us to set the slug in the Markdown itself, and only fall back to auto-generation. At the same time, we push the article objects into an array, so we have something easy to iterate over in our component.

Extra Credit

There are two things you’ll probably want to do right away if you use this method. First is to sort by date and second is to filter by article status (i.e. draft and published). Since we already have an array, this can be done in one line, added just before return articleArray:

articleArray.filter(post => post.status === 'publish').sort((a, b) => a.created < b.created);

Final Step

One last thing to do now, and that’s instruct our YesArticle component to use the new data we’re receiving along with the route change:

// ./src/components/YesArticle.vue
// ...
data() {
  return {
    article: require(`../posts/${this.id}.md`), // eslint-disable-line global-require, import/no-dynamic-require
  };
},

Since we know that our component will be pre-rendered, we can disable the ESLint rules that disallow dynamic and global requires, and require the path to the post that matches the id parameter. This triggers our Webpack Markdown loaders, and we’re all done!

OMG!

Go ahead and test this out:

$ npm run build && cd dist && python -m SimpleHTTPServer

Visit localhost:8000, navigate around and refresh the pages to load the whole app from the new entry point. It works!

I want to emphasize just how cool this is. We’ve turned a folder of Markdown files into an array of objects that we can use as we wish, anywhere on our website. The sky is the limit!

If you want to just see how it all works, you can check out the final branch:

$ git checkout step-complete

Extend functionality with plugins

My favorite part about this technique is that everything is extensible and replaceable.

Did someone create a better Markdown processor? Great, swap out the loader! Need control over your site’s SEO? There’s a plugin for that. Need to add a commenting system? Add that plugin, too.

I like to keep an eye on these two repositories for ideas and inspiration:

Profit!

You thought this step was a joke?

The very last thing we’ll want to do now is profit from the simplicity we’ve created and nab some free hosting. Since your site is now being generated on your git repository, all you really need is to do is push your changes to Github, Bitbucket, Gitlab or whatever code repository you use. I chose Gitlab because private repos are free and I didn’t want to have my drafts public, even in repo-form.

After that's that set up, you need to find a host. What you really want is a host that offers continuous integration and deployment so that merging to your master branch triggers the npm run build command and regenerates your site.

I used Gitlab’s own CI tools for the first few months after I set this up. I found the setup to be easy but troubleshooting issues to be difficult. I recently switched to Netlify, which has an outstanding free plan and some great CLI tools built right in.

In both cases, you’re able to point your own domain at their servers and even setup an SSL certificate for HTTPS support—that last point being important if you ever want to experiment with things like the getUserMedia API, or create a shop to make sales.

With all this set up, you’re now a member of the Butt-less Website club. Congratulations and welcome, friends! Hopefully you find this to be a simple alternative to complex content management systems for your own personal website and that it allows you to experiment with ease. Please let me know in the comments if you get stuck along the way...or if you succeed beyond your wildest dreams. 😉


The Rise of the Butt-less Website is a post from CSS-Tricks



from CSS-Tricks http://ift.tt/2DxFakG
via IFTTT

3 Creative Ways to Give Your Content Efforts a Boost - Whiteboard Friday

Posted by randfish

We know that content is our doorway to earning countless SEO benefits for our sites. Admittedly, though, it’s too easy to get stuck in a rut after one too many content marketing campaigns. In this extra-special holiday edition of Whitebeard Friday (see what we did there?), Rand offers three novel ways to add sparkle to your content creation efforts

3 way to give your content efforts a boost

Click on the whiteboard image above to open a high resolution version in a new tab!


Video Transcription

Howdy, Moz fans, and welcome to a special Christmas edition of Whitebeard Friday. This week, I wanted to try and help with just a few tactical suggestions on some creative ways to pump up those content marketing campaigns.

I've seen that many, many folks in the SEO world, of course, naturally, are investing in content marketing because content is the path to links and amplification and search traffic. Sometimes those content campaigns can feel a little stale or repetitive. So I have some creative ideas, things that I've seen some people executing on that I think we might be able to leverage for some of our work.

1. Niche groups

First one, if you can identify in your community these sort of small but vocal niche groups that are . . . when I say your community, it doesn't have to be people you already reach. It can be people inside the community of content generation and of topical interest around your subject matter. Then help them to amplify their voices or their causes or their pet projects, etc.

So I'll use the example of being in the foodie and gourmand world. So here's a bunch of foodies. But this particular tiny group is extremely passionate about food trucks, and, in particular, they really hate the laws that restrict food truck growth, that a lot of cities don't allow food trucks to be in certain spaces. They have to jump through a bunch of hoops to get licensed. They are not permitted to be permanently in a place for a whole week. Whatever it is, whatever those legal restrictions are. So by serving this small group, you might think that content is way too niche.


The wonderful part is that content is the kind that gets amplified very loudly, very repetitively, that can help you earn links and traffic to this small community. If that community is small and loud and feels like their voices aren't being heard elsewhere, you can build some great brand advocacy inside that group as well. By the way, I would urge you to be authentic, choose causes that you or your company also care about. Don't just pick something at random.

2. Products and services

Second, if you can, try and seek out products and services that your audience uses or needs, but that doesn't actually directly conflict with your business. Then create a resource that lists or rates or ranks and recommends those top choices. We've actually done this a few times at Moz. I have this recommended list of agency and consultant providers, but Moz does not compete with any of those. But it's a helpful list. As a result of listing those folks and having this sort of process around it, many of those people are pumping up that content.

Now here's another example. Foodie Moz, Foodie Moz sounds like a great domain. I should go register that right after this hat stops hitting me in the back of the head. I don't know how Santa deals with that. So Foodie Moz presents the best cookbooks of 2017. Now, Foodie Moz might be in the food and recipe world. But it turns out, the wonderful part is cookbooks are something that is used by their audience but not directly conflicting with them.

Since it's not self-promotional, but it is useful to your audience, the likelihood that you can earn links and amplification because you seem like a non-self-interested party is much greater. You're providing value without asking anything in return. It's not like anyone buying these cookbooks would help you. It's not like you have some ulterior motive in ranking this one number one or that one number two. You're merely putting together an unbiased set of resources that help your audience. That is a great way to get a piece of content to do well.

3. Content creators

Third, last but not least here, if you can, find content creators who have been very successful. You can recruit them, the people who have had hit pieces, to create content for your brand. In a lot of ways, this is like cheating. It's almost like buying links, except instead of buying the links, you're buying the time and energy of the person who creates content that you have high likelihood or high propensity for being successful in that content niche with what they create because of their past track record and the audience they've already built.

Pro-tip here. Journalists and media contributors, even contributors to online media, like a BuzzFeed or something like that, are great targets. Why? Well, because they're usually poorly paid and they are desperate to build a portfolio of professional work. Some of these folks are insanely talented, and they already have networks of people who have liked their work in the past and have helped amplify them.

So if you can use a tool like BuzzSumo — that would be generally what I'd recommend, there's a few others, but BuzzSumo is really great for this — you can search for, for example, recipes and see the most shared content in the recipe world in, say, the last three months. Then we can identify, "Oh, here we go. This person wrote the hardest recipe challenge gifts. Oh, all right. That did really, really well. I wonder if we can see who that is. Oh look, she does freelance work. I bet she can write for us."

It's like cheating. It's a great hack. It's a great to way to recruit someone who you know is likely to have a great shot at their work doing well, give them the freedom to write what they want, to create what they want, and then host it on your site. A great way to do content creation, for a decent price, that has a high likelihood of solid amplification.

All right, everyone, look forward to some of your thoughts and tactics. For those of you who celebrate Christmas, a Merry Christmas from all of us at Moz. For those of you who celebrate Hanukkah, happy belated Hanukkah. I know that I'm filming this during Hanukkah, but it's probably after Hanukkah that you're seeing it. For those of you who are celebrating any other holiday this year, a very happy holiday season to you. We look forward to joining you again next week for another edition of Whiteboard Friday. Take care.

Video transcription by Speechpad.com


Sign up for The Moz Top 10, a semimonthly mailer updating you on the top ten hottest pieces of SEO news, tips, and rad links uncovered by the Moz team. Think of it as your exclusive digest of stuff you don't have time to hunt down but want to read!



from The Moz Blog http://ift.tt/2CU1NOQ
via IFTTT

Passkeys: What the Heck and Why?

These things called  passkeys  sure are making the rounds these days. They were a main attraction at  W3C TPAC 2022 , gained support in  Saf...