Introduction • Features • Technical Details • Development and Deployment • Future Improvements • Acknowledgements & Version History • Using This Project • License • Contributing • Contact
I'm product designer with +10 years of experience, based in Tokyo, Japan (prev in San Francisco).
Why all this for a website?
- It's a personal website. It's one of the few times you can build what you want without compromises.
- I heard all of my eng friends saying how fast you can build features in elixir/phoenix, and I wanted to build something that automated the pain from my last portfolio, with more infra and a better DX (e.g. admin interface, metrics, gitops)
- Maybe it'll be reusable for future app development
- This is also a homelab project
- Hundreds of tests with good coverage
- Security checks with Sobelow
- Static code analysis with Dialyzer
- Automated code formatting and linting for elixir, javascript, css & bash
- Git hooks for pre-commit and pre-push checks
You can read more in Development and Deployment
The portfolio supports both English (/en) and Japanese (/ja) languages. It determines the locale, by in order:
- Locale in URL
- Accept-Language header to detect the user's preferred language.
For application and landing page content, we use Gettext for translations. Case studies and other long form content have separate markdown files for each language.
You can also add a new language in /gettext/ for UI and /priv/content/schema/ for whichever schema you want to translate.
An admin interface with a Markdown-based system allows for creating case studies with live reloading. This feature is only accessible in the development environment and is not exposed in production.
A optically-aligned typographic "engine" that optically adjusts typgographic elements. The goal is to ensure that the container aligns precisely with the visible text content—from the ascender down to the bottom of the x-height—rather than encompassing the entire line-height box. This precise alignment makes it so all objects are equally spaced optically.
It requires specific integration for each typeface you use, so it's not practical for most teams.
In the future, you can get this for free if CSS Inline Layout Module Level 3 is implemented.
The backend is built with Elixir and Phoenix. PostgreSQL is used as the database for storing project data and user interactions. Obviously overkill for what's basically a website, but I hope to use the infra for other projects.
On the frontend, we use:
- esbuild: An extremely fast JavaScript bundler and minifier
- TailwindCSS: A utility-first CSS framework
- Heroicons: For SVG icons
PostgreSQL is used for data storage. A file watcher looks for markdown files with the correct frontmatter key-value pairs to update records in the database.
To set up the project locally:
- Clone the repository
- Ensure you have Docker installed
- Run
docker-compose up -dto start the application - Run
./run mix ecto.setupto initially set up the db - Visit
localhost:8000in your browser
This project uses Lefthook for managing Git hooks to ensure code quality and consistency. The configuration can be found in .lefthook.yml. Here's a brief overview of what's included:
- Pre-commit hooks: Format Elixir files and ensure containers are running.
- Pre-push hooks: Run format checks, linting (Credo), and tests for Elixir code.
To use these hooks, install Lefthook by following the instructions at Lefthook's GitHub repository.
These hooks help maintain code quality, but you bypass the checks easily if you want to do it all in CI workflows.
Deployment details are still being finalized. The project uses GitHub Actions for CI/CD, as indicated by the workflow status badge at the top of this README. Below is a WIP:
You'll need to make sure the env variables are set to production, namely:
export DOCKER_WEB_VOLUME=./priv/static:/app/priv/static # Use this for production
#export DOCKER_WEB_VOLUME=.:/app # use this for dev If you encounter issues running mix ecto.drop while the app is running, try stopping the web app first:
docker compose stop webIf that doesn't work:
docker exec -it YOURAPP-postgres-1 psql -U YOURUSER -d YOURDATABASE -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'YOURDATABASE' AND pid <> pg_backend_pid();"Then drop your database:
docker exec -it YOURAPP-postgres-1 psql -U YOURAPP -d postgres -c "DROP DATABASE YOURDATABASE;"and the test database as well (otherwise they'll be out of sync)
docker exec -it YOURAPP-postgres-1 psql -U YOURAPP -d postgres -c "DROP DATABASE YOURDATABASE_test;"After that, run your app again with docker compose up -d and then do ./run mix ecto.setup.
Planned enhancements include:
- Image optimization and minification
- OG graph image generation
- Performance optimizations
- Full implementation of telemetry
A special thanks to Nick Janetakis for creating the docker-phoenix-example, which served as the foundation for this portfolio.
Previous portfolio versions:
- 2016 – 2024: Built using React, NextJS, and Styled-Components. Code available here.
- 2014 – 2016: Built using Vanilla JS. View on Wayback Machine.
- 2010: Built using Flash.
This project is licensed under AGPL-3.0. See the LICENSE file for details.
If you're using this project as a base for your own portfolio website, I ask that you provide credit for the original work. Here's how you can do that:
- Include a comment in your main layout file (e.g.,
root.html.heex) that says:
<!-- Based on Zane Riley's Portfolio: https://github.com/zaneriley/personal-site -->
- Add a line to your README.md file:
This project is based on [Zane Riley's Portfolio](https://github.com/zaneriley/personal-site).By providing credit, you help support open-source projects and allow others to discover and learn from the original work. Thank you for your consideration!
