Packages

Static site builder for Elixir lovers

Current section

Files

Jump to
vox README.md
Raw

README.md

# Vox
The static site generator for Elixir lovers.
## Quick start
There is a new site generator that is the quickest way to get started.
In order to use it, run
```bash
mix archive.install hex vox_new
mix vox.new blog
```
This will generate a simple scaffolded site that you can customize and tweak as you see fit.
You can also pass a path (e.g. `mix vox.new nested/dir/blog`); the app name comes from the name of the target directory.
There is also an example blog built with Vox that attempts to take advantage of all the features of Vox as they are built.
You can look at the source code for that (and clone it and build it yourself) at [geolessel/vox-example](https://github.com/geolessel/vox-example).
### `vox.new` options
- `--esbuild` - include a simple esbuild installation for asset compilation
## Manual installation
The package can be installed by adding `vox` to your list of dependencies in `mix.exs`.
For now, it is recommended to install directly from GitHub.
**Note that the `mix vox.new` generator will generate the `"~> 0.1.0"` version instead.**
```elixir
def deps do
[
{:vox, github: "geolessel/vox"}
]
end
```
## Usage
Configure vox in your app (`config/config.exs`):
```elixir
import Config
config :vox, src_dir: "site", output_dir: "_html"
```
Then start writing!
Any file with a `.eex` extension will be compiled and your directory structure beneath your `src_dir` will be retained.
For example, if I have a file named `site/blog/posts/2023/hello-world.html.eex` then that file will be compiled into `_html/blog/posts/2023/hello-world.html`.
To build your site, run `mix vox.build`.
To start a dev server that rebuilds the site on filesystem changes, run `mix vox.dev`.
**Note that for now, you will have to manually reload the page you are on to see the changes.**
Automatic reloading is on the wishlist.
### Root template
**You must have a `src_dir/_root.html.eex` file defined**.
This file contains code that will be in _every_ rendered html file.
Typically, this will contain your initial `html`, `head`, and `body` tags.
Render your further child template with
```elixir
<%= @inner_content %>
```
### Template files
For now, **you must have a `src_dir/_template.html.eex` file defined**.
The compiler will use that file as the default template.
Template files are never built into their own page; they are read directly from disk and wrapped around the pages that use them (see "Private files" below).
There are two other ways you can set the template for a file.
1. Bind `template` in the file's bindings. This is a path relative to the file it is bound in.
```elixir
<%
title = "Special post"
template = "_special_template.html.eex"
%>
```
2. Change the default of an entire subdirectory by creating a new `_template.html.eex` in it.
When a file is compiled, it will automatically look in its current directory for a `_template.html.eex` file and us that if it exists.
If it does not exist, it will search in the parent directory... and so on until it reaches `src_dir/_template.html.eex`
Use `<%= inner_content %>` inside this file to render the content of other files.
### Passthrough files
There are some files that need to be included in a site's build but should not be processed (like images, fonts, and other assets).
Any file with an extension other than `.eex` or `.html` will not be processed.
Instead, they will be copied over in the same directory structure and file name as it is in the source directory.
For example, if there was an image located on your filesystem at `src_dir/images/logo.png`, it will be available in the generated site structure are `dest_dir/images/logo.png`.
### Private files
Files whose name starts with `_` (like `_root.html.eex` and `_template.html.eex`) and any file inside a directory named `partials` are never compiled or copied into the output.
Templates and partials are read directly from disk by path when they're needed, so they don't need to be built as pages themselves.
This is how templates and partials stay out of your built site.
`Vox.partial/2` accepts any path, so keep partial files inside a `partials` directory (or give them a `_` prefix) or they will be built as pages.
### File metadata (also known as front matter or bindings)
At the top of your `.eex` files, set up the file's front matter inside of `<% ... %>` blocks.
If you want to set metadata for the file, it needs to be at the top of the `.eex` file.
For now, the syntax **is finicky** and needs to be precise.
1. The first line should be a lone `<%`
2. Your metadata or bindings will be next. Feel free to use any [Elixir data types](https://hexdocs.pm/elixir/syntax-reference.html#data-types).
3. The last line of your metadata declarations should be a lone `%>`
With these set, you can access them outside (or inside) of this file through map syntax.
Take the following file as an example:
```elixir
<%
title = "Vox helped me build this"
author = %{
name: "Geoffrey Lessel",
site: "https://builditwithphoenix.com"
}
collections = :posts
%>
```
If you were to iterate through your `for post <- @posts do ...` collection, you can now access this information like `post.author.name`.
#### Collections
One special metadata (binding) you can use is `collection`.
This will put the file in a special collection you can reference later in your templates or other `.eex` files.
There will be an assign for every collection value you bind in your `.eex` files.
For example, consider the following two files.
First is a blog post specifying it belongs in the `:posts` and `:elixir` collections.
```elixir
<%
title = "Elixir is cool"
date = ~D[2023-05-05]
collections = [:posts, :elixir]
%>
<h1>Elixir is my favorite language</h1>
... lots of must-read content ...
```
Second is an index page listing out all the blog posts and links to their pages.
The `@posts` and `@elixir` are already bound and ready to use because they were indicated in one or more `collections`.
```elixir
<h1>Here are all my blog posts</h1>
<ul>
<%= for post <- @posts do %>
<li>
<a href="<%= post.destination_path %>"><%= post.title %></a>
</li>
<% end %>
</ul>
```
### Partials
If you have a bit of code or markup that is reused often in your site, it might be beneficial to put it into a partial.
To render the contents of a partial file, use the **Vox.partial/1** function passing the full path of the file from your source directory.
If your partial needs access to variables your current page has access to, pass them in as the optional second argument.
To pass on _all_ the assigns that your current page has access to, simply pass `assigns` as the second argument.
For example, if you specified your source directory as `my-blog` and you had a partial file located on your filesystem at `my-blog/partials/copyright.html`, you would insert it into an EEx file like...
```elixir
<%= Vox.partial("/partials/copyright.html") %>
```
To let your partial have access to all currently known assigns:
```elixir
<%= Vox.partial("/partials/header.html.eex", assigns %>
```
Note that it is important and required to pass the initial slash `/` in the path.