How to create a WordPress site from scratch without a theme

How many times have you wonder how great it will be to be able to have an idea and make a WordPress site right away?
I’m a marketer by training, so I know how complicated it may look. But in the end, it’s not that hard
In this post I’ll guide throughout the process of getting started with WordPress from scratch, so you can have something up and running all by yourself.
When I got started I wish I’d found a guide like this one. Maybe there was one, I didn’t find it though. So it took me a while to learn these basic things that other guides usually skip (but if you find one that doesn’t email it to me and I’ll link it in other resources). So I hope this is useful for you and you can skip a few weeks of frustration.
This is a step-by-step guide. It’s done in a way that it’s easier to follow up. If you’re an experienced programmer you might find it too slow, but that’s the purpose, I want to make it as easy and clear as possible so everybody can follow through.
Prerequisite: HTML and CSS. Not to a master degree, but just enough so you know what we’re talking about.
How can you learn HTML & CSS fast? Get this book. It’ll take you between 10 or 20 hours, but that’s all you need to know. We’ll cover the rest in this guide.

1) How a website is structured?

html wordpress bootstrapCredits: Lee Robertson

Before we get into WordPress, you need to know how the structure of a simple website looks like.
A website is a bunch of files.
And each one has a simple purpose.
For example, in a basic HTML site, inside a folder we’d have some .html files, where each of them represents a page of the site.
We’d also have some folders that contain different assets, like:

  • /css: Every .css file. They’re the styling. Usually there’s one called style.css
  • /js: Every .js (javascript) file. For effects (and much more–I know.)
  • /img: for images you want to link.

And maybe you’d have another one for other kind of asset like fonts.
We don’t have to get too much into detail here, but inside every html file you need to link those assets into your structure files (.html).
How a HTML document works:

  1. Header
  2. Body
  3. Footer

CSS and JavaScript go in the header and footer (respectively)
You can write them for each html document, but the thing here is to create separate files for the header and footer, and recall them in each document (more on this later).
How do you recall them?
It’s simple, just tell the browser which documents you want to link.
For CSS: (in the header)

<link href="css/style.css" rel="stylesheet">

For JS (in the footer)

<script src="js/example.js"&gt;&lt;/script>

That’s about it. Now you know how a website is structured in a simple way and you’re ready to go.

2) What’s Bootstrap?

[I’m going to refer here to Bootstrap’s v3.3.7, but there’s v4 already. Feel free to use the newest version.]
Bootstrap is a CSS Framework. Basically it makes your life easier. It offers a predefined set of styling things that saves you time and gives you a nice structure to work with.
There are others but this is the most common one.
In fact, most WordPress pre-built themes use Bootstrap.
The key here though, is to know what it actually is, how it works, and how you can overwrite their styling and customize yours.
If you go to you can get to know more in depth about it, but you don’t have to.
There are several ways to integrate Bootstrap in your site. There are of course plugins and others, but I’m not a big fan of them. I prefer to have control over the files and avoid linking other sites to my own if possible.
So should you.
How do you do that?
Download the Bootstrap’s source files here (scroll down to the bottom and click ‘Compile and Download’). Then you put them with your web files and link them in the every HTML document. (We’ll get to how to do that on WordPress in a minute.)
That’s it.
(Just a quick note, and we’ll get into details later in this post: Don’t overwrite these files. We’ll create different files to change things, but leave them as they come)
Before we get into building a WordPress theme, you need to know how Bootstrap works. You should know the basics, but I encourage you to take a look at their documentation.
Basically, what you do is to recall Bootstrap CSS styling through classes in HTML.
For example, if you want to create a responsive section with a the right margins and padding, you’d write:

<section class="container"></section>

.container is a CSS class within the Bootstrap files that tells the browser how to style it. The beauty of it is that it fits in every screen.
You should check Bootstrap’s documentation, there are lots of things you can do with it, such us: modifying layouts, playing with grids, buttons, dropdowns, navbars, thumbnails, alerts, anything related with responsiveness…
The way you include anything from Bootstrap is to play in HTML elements and classes.

3) How a WordPress theme works

You’re starting to put things into place, so now it’s time to understand how you can play with a WordPress theme.
What is WordPress?
I’m not going to give you a thorough explanation, but a quick idea so you can move forward.
WordPress is a CMS (Content Management System), which means that is a pre-built platform that manages most of the things that happens in the back-end, so you don’t have to do anything, and just interact with an admin panel that makes your life easier.
When you download WordPress source files you’d notice that there are three folders (and a bunch of files):

  • /wp-admin
  • /wp-content
  • /wp-includes

Just focus on the folder /wp-content, forget about the other two.
Understanding /wp-content is really simple, especially if you’ve already used WordPress. Within that folder you’ve got two more folders: (1) /plugins and (2) /themes.
Forget about plugins, that’s something you can manage through the admin interface. What’s important for you is the themes folder.
Once you open the themes folder you’ll see different folders with some default themes. You can open any of them and take a look, and you’ll find some similarities with the HTML structure we saw in #1.
The difference though, is that there are not HTML, but PHP. Plus a bunch of other files you don’t understand yet.
Some files you need to know right away:

  1. front-page.php : Probably you don’t see it now, but you’ll create one. That one will be your home page.
  2. index.php : the main view for you blog page.
  3. single.php : that’s the view of a single post.
  4. content.php : the content of the post (not the view of the whole post page.)
  5. header.php : separate header
  6. footer.php : separate footer
  7. style.css
  8. Maybe some other folders like /css and /js

Now we can get into detail.

4) Build your own WordPress Theme.

I hate pre-built themes. Yes, they are great and allow you to quickly get something up and running, but you end up making more changes overwriting everything and using multiple plugins that just make your site slower. And of course everybody has the same ones: Divi, Colorlib…
Nevertheless, you don’t have to start from scratch either. There’s a fantastic blank theme called BlankSlate (if you find another one that does the same thing, it’s fine.)
This theme takes care of everything that happens in the back-end, so you just have to work in the front-end. It’s like running a restaurant and having someone who’s managing the kitchen so you can focus on running the show!

Consider installing a localhost
Now you should consider installing a localhost in your computer. While you can preview HTML files in your browser without problem, WordPress is different (because it needs a database to run the site). So here you have two options:

  1. Upload directly your theme folder to your WordPress site (remember it has to be a .zip file). The problem? You need Internet connection and upload the theme again and again with each new change.
  2. RECOMMENDATION: Find out how to install a localhost with Apache in your computer. I use Linux so I can’t help you with that (and if you use Linux you already know how to do that.) The good news is that you don’t need Internet and you can see any change right away. Spend some time figuring this out, it’s totally worth it.

The only way option 1 is actually an option, is if you have ftp access to your server and are able to mod your files right away. To figure this out, type in any search engine how to access my ftp with [name of your provider]. You can download Filezilla (a ftp manager).

Integrate Bootstrap in your theme

If you haven’t downloaded Bootstrap, go and do it now here. That’s the default setting, but you can modify it and when you’re done scroll down and hit ‘Compile and Download’.
You will get three folders: /css, /js and /fonts. Now, download BlankSlate (if you haven’t done it), extract it and put that folder call blankslate inside themes folder. (Or you can also upload it from your admin panel, but I’m considering you have a localhost in your computer up and running.)
Alright, go to /blankslate and paste Bootstrap folders there: /css, /js and /fonts.

Linking Bootstrap

All you have to do now is to tell your theme to grab the bootstrap files. How do you do that? By modifying header.php and footer.php.
Important: Almost always you’ll use the same header and footer in every page. However, if you somehow decide you want a different header or footer, remember to include the following lines.
Open the file header.php, delete everything and paste this code:

<!DOCTYPE html>
<html <?php language_attributes(); ?>>
    <meta charset="<?php bloginfo( 'charset' ); ?>" />
    <meta name="viewport" content="width=device-width" />
    <meta name="description" content="<?php bloginfo('description'); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
          <?php wp_title(); ?>
    <link href="<?php bloginfo('template_url'); ?>/css/bootstrap.css" rel="stylesheet">
    <link href="<?php bloginfo('template_url'); ?>/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="<?php echo get_stylesheet_uri(); ?>" />
    <?php wp_head(); ?>
  <body <?php body_class(); ?>>
      <nav class="navbar navbar-default" role="navigation">
      	<div class="container">
      		<!-- Brand and toggle get grouped for better mobile display -->
      		<div class="navbar-header">
      			<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
      				<span class="sr-only">Toggle navigation</span>
              		<span class="icon-bar"></span>
              		<span class="icon-bar"></span>
              		<span class="icon-bar"></span>
            <a class="navbar-brand" href="/">Brand</a>
              wp_nav_menu( array(
                  'menu'              => 'primary',
                  'theme_location'    => 'primary',
                  'depth'             => 2,
                  'container'         => 'div',
                  'container_class'   => 'collapse navbar-collapse',
                  'container_id'      => 'bs-example-navbar-collapse-1',
                  'menu_class'        => 'navbar-right',
                  'fallback_cb'       => 'WP_Bootstrap_Navwalker::fallback',
                  'walker'            => new WP_Bootstrap_Navwalker())
    <section class="container">

Open your footer.php, delete everything and paste this code:

      <footer class="container" id="footer" role="contentinfo">
        <?php wp_nav_menu( array( 'theme_location' => 'main-menu', 'menu_class' => 'footer-links', ) ); ?>
    <?php wp_footer(); ?>
    <script src=""></script>
    <script src="<?php bloginfo('template_url'); ?>/js/bootstrap.js"></script>
    <script src="<?php bloginfo('template_url'); ?>/js/bootstrap.min.js"></script>

Now in the /blankslate folder create a file called wp-bootstrap-navwalker.php (you can see the documentation here) and paste this code in the file (this is to be able to modify the manu from the admin panel (you have to scroll quite a bit here…):

 * WP Bootstrap Navwalker
 * @package WP-Bootstrap-Navwalker
 * Class Name: WP_Bootstrap_Navwalker
 * Plugin Name: WP Bootstrap Navwalker
 * Plugin URI:
 * Description: A custom WordPress nav walker class to implement the Bootstrap 3 navigation style in a custom theme using the WordPress built in menu manager.
 * Author: Edward McIntyre - @twittem, WP Bootstrap
 * Version: 2.0.5
 * Author URI:
 * GitHub Plugin URI:
 * GitHub Branch: master
 * License: GPL-3.0+
 * License URI:
/* Check if Class Exists. */
if ( ! class_exists( 'WP_Bootstrap_Navwalker' ) ) {
	 * WP_Bootstrap_Navwalker class.
	 * @extends Walker_Nav_Menu
	class WP_Bootstrap_Navwalker extends Walker_Nav_Menu {
		 * Start Level.
		 * @see Walker::start_lvl()
		 * @since 3.0.0
		 * @access public
		 * @param mixed $output Passed by reference. Used to append additional content.
		 * @param int   $depth (default: 0) Depth of page. Used for padding.
		 * @param array $args (default: array()) Arguments.
		 * @return void
		public function start_lvl( &$output, $depth = 0, $args = array() ) {
			$indent  = str_repeat( "\t", $depth );
			$output .= "\n$indent<ul role=\"menu\" class=\" dropdown-menu\" >\n";
		 * Start El.
		 * @see Walker::start_el()
		 * @since 3.0.0
		 * @access public
		 * @param mixed $output Passed by reference. Used to append additional content.
		 * @param mixed $item Menu item data object.
		 * @param int   $depth (default: 0) Depth of menu item. Used for padding.
		 * @param array $args (default: array()) Arguments.
		 * @param int   $id (default: 0) Menu item ID.
		 * @return void
		public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
			$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
			 * Dividers, Headers or Disabled
			 * =============================
			 * Determine whether the item is a Divider, Header, Disabled or regular
			 * menu item. To prevent errors we use the strcasecmp() function to so a
			 * comparison that is not case sensitive. The strcasecmp() function returns
			 * a 0 if the strings are equal.
			if ( 0 === strcasecmp( $item->attr_title, 'divider' ) && 1 === $depth ) {
				$output .= $indent . '<li role="presentation" class="divider">';
			} elseif ( 0 === strcasecmp( $item->title, 'divider' ) && 1 === $depth ) {
				$output .= $indent . '<li role="presentation" class="divider">';
			} elseif ( 0 === strcasecmp( $item->attr_title, 'dropdown-header' ) && 1 === $depth ) {
				$output .= $indent . '<li role="presentation" class="dropdown-header">' . esc_attr( $item->title );
			} elseif ( 0 === strcasecmp( $item->attr_title, 'disabled' ) ) {
				$output .= $indent . '<li role="presentation" class="disabled"><a href="#">' . esc_attr( $item->title ) . '</a>';
			} else {
				$value       = '';
				$class_names = $value;
				$classes     = empty( $item->classes ) ? array() : (array) $item->classes;
				$classes[]   = 'menu-item-' . $item->ID;
				$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
				if ( $args->has_children ) {
					$class_names .= ' dropdown';
				if ( in_array( 'current-menu-item', $classes, true ) ) {
					$class_names .= ' active';
				$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
				$id          = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args );
				$id          = $id ? ' id="' . esc_attr( $id ) . '"' : '';
				$output     .= $indent . '<li itemscope="itemscope" itemtype=""' . $id . $value . $class_names . '>';
				$atts        = array();
				if ( empty( $item->attr_title ) ) {
					$atts['title'] = ! empty( $item->title ) ? strip_tags( $item->title ) : '';
				} else {
					$atts['title'] = $item->attr_title;
				$atts['target'] = ! empty( $item->target ) ? $item->target : '';
				$atts['rel']    = ! empty( $item->xfn ) ? $item->xfn : '';
				// If item has_children add atts to a.
				if ( $args->has_children && 0 === $depth ) {
					$atts['href']          = '#';
					$atts['data-toggle']   = 'dropdown';
					$atts['class']         = 'dropdown-toggle';
					$atts['aria-haspopup'] = 'true';
				} else {
					$atts['href'] = ! empty( $item->url ) ? $item->url : '';
				$atts       = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );
				$attributes = '';
				foreach ( $atts as $attr => $value ) {
					if ( ! empty( $value ) ) {
						$value       = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
						$attributes .= ' ' . $attr . '="' . $value . '"';
				$item_output = $args->before;
				 * Glyphicons/Font-Awesome
				 * ===========
				 * Since the the menu item is NOT a Divider or Header we check the see
				 * if there is a value in the attr_title property. If the attr_title
				 * property is NOT null we apply it as the class name for the glyphicon.
				if ( ! empty( $item->attr_title ) ) {
					$pos = strpos( esc_attr( $item->attr_title ), 'glyphicon' );
					if ( false !== $pos ) {
						$item_output .= '<a' . $attributes . '><span class="glyphicon ' . esc_attr( $item->attr_title ) . '" aria-hidden="true"></span> ';
					} else {
						$item_output .= '<a' . $attributes . '><i class="fa ' . esc_attr( $item->attr_title ) . '" aria-hidden="true"></i> ';
				} else {
					$item_output .= '<a' . $attributes . '>';
				$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
				$item_output .= ( $args->has_children && 0 === $depth ) ? ' <span class="caret"></span></a>' : '</a>';
				$item_output .= $args->after;
				$output      .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
			} // End if().
		 * Traverse elements to create list from elements.
		 * Display one element if the element doesn't have any children otherwise,
		 * display the element and its children. Will only traverse up to the max
		 * depth and no ignore elements under that depth.
		 * This method shouldn't be called directly, use the walk() method instead.
		 * @see Walker::start_el()
		 * @since 2.5.0
		 * @access public
		 * @param mixed $element Data object.
		 * @param mixed $children_elements List of elements to continue traversing.
		 * @param mixed $max_depth Max depth to traverse.
		 * @param mixed $depth Depth of current element.
		 * @param mixed $args Arguments.
		 * @param mixed $output Passed by reference. Used to append additional content.
		 * @return null Null on failure with no changes to parameters.
		public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
			if ( ! $element ) {
				return; }
			$id_field = $this->db_fields['id'];
			// Display this element.
			if ( is_object( $args[0] ) ) {
				$args[0]->has_children = ! empty( $children_elements[ $element->$id_field ] ); }
			parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
		 * Menu Fallback
		 * =============
		 * If this function is assigned to the wp_nav_menu's fallback_cb variable
		 * and a menu has not been assigned to the theme location in the WordPress
		 * menu manager the function with display nothing to a non-logged in user,
		 * and will add a link to the WordPress menu manager if logged in as an admin.
		 * @param array $args passed from the wp_nav_menu function.
		public static function fallback( $args ) {
			if ( current_user_can( 'edit_theme_options' ) ) {
				/* Get Arguments. */
				$container       = $args['container'];
				$container_id    = $args['container_id'];
				$container_class = $args['container_class'];
				$menu_class      = $args['menu_class'];
				$menu_id         = $args['menu_id'];
				if ( $container ) {
					echo '<' . esc_attr( $container );
					if ( $container_id ) {
						echo ' id="' . esc_attr( $container_id ) . '"';
					if ( $container_class ) {
						echo ' class="' . esc_attr( $container_class ) . '"'; }
					echo '>';
				echo '<ul';
				if ( $menu_id ) {
					echo ' id="' . esc_attr( $menu_id ) . '"'; }
				if ( $menu_class ) {
					echo ' class="' . esc_attr( $menu_class ) . '"'; }
				echo '>';
				echo '<li><a href="' . esc_url( admin_url( 'nav-menus.php' ) ) . '" title="">' . esc_attr( 'Add a menu', '' ) . '</a></li>';
				echo '</ul>';
				if ( $container ) {
					echo '</' . esc_attr( $container ) . '>'; }
} // End if().

Congratulations! Now you’re all set. All you have to do is to code some HTML using the Bootstrap documentation to help you.
You can either code your pages directly into the PHP files or using the ‘Text’ tab when you create a page or a post.

5) Some extra tips and thoughts

Search stuff on StackOverflow. You don’t have to be a great coder or even understand PHP. Just go to StackOverflow, and search for your problem. There’s an answer for it (or you just ask!)
Use as few plugins as possible. Make your site lean and fast. Some plugins are really helpful but more often than not are a burden.
Knowing how to code is not the most important thing. Having a good eye for design is critical, and that’s what will make your website to stand out. The first thing you should know about design is that is not how fancy something looks, or how many colors it has: design is having a problem and being able to solve it.
SET UP a SSL certificate: if you’re not doing anything related with eCommerce, go with Let’s Encrypt. It’s free and great. The problem? You’ve got to regenerate it every 90 days (they recommend 60 days, though). You can do it manually (once you learn how to do it, it doesn’t take more than 10 minutes) or you can automate it.
Explore source files on websites: Not just HTML, but CSS and try to understand how it works. Type Ctrl/Cmd + U and search for a file named style.css (Ctrl/Cmd + F to look for it) and take a look. Do it on my site, and copy anything you need that might help you. Not credits require (I’d appreciate them though).
Got questions? Email me.