Week 12: Building a Github Website
We will build static websites using Quarto and GitHub Pages. We will learn how to render text, code, visuals, and interactive visuals on the website.
To understand how websites are built in Quarto, we first must understand a bit about how Quarto documents are rendered to produce HTML output.
It is not essential to understand all the inner workings of this process to be able to create a website. However, it is important to understand which component is responsible for what since this will make it easier to target appropriate help files when you need them!
Quarto combines several different processes together to create documents. The basic workflow structure for a Quarto document:
.qmd → (jupyter) → .md → (Pandoc) → HTML / PDF / etc.
Quarto: A publishing system built on Pandoc, supporting Python, R, Julia, and Observable JS.
jupyter: Executes Python code chunks and weaves results back into the document.
Pandoc: A “universal” document converter that converts files between numerous formats including Markdown into HTML, PDF, Word, and many other formats.
Pandoc comes bundled with Quarto (no separate install needed).
Rendering Quarto documents consists of:
.md.md to the desired output format with PandocThe workflow in more detail:
.qmd document contains YAML metadata, text (Markdown), and code chunks..md file..md file, applying the output format and any templates or themes specified in the YAML.format: field (e.g., html, revealjs, pdf) determines the output.quarto render (CLI) or the Render button in VS Code.Along the way, we add structure and style. A wide range of tools helps us in this process — including Cascading Style Sheets (CSS), raw HTML, and Pandoc/Quarto templates.
YAML = Yet Another Markup Language YAML Ain’t Markup Language
It is a data-oriented language structure used as the input format for diverse software applications. It is not a markup language but a serialization language — it converts objects into a transmittable/storable format (a series of bits) that can later be reconstructed.
A typical Quarto YAML header:
The YAML metadata affects the code, content, and the output. It is processed by jupyter, Quarto, and Pandoc.
The title and author fields are processed by Pandoc to set template variables.
The format field is used by Quarto to determine the output format and apply the appropriate rendering pipeline.
Note that not all Quarto documents are processed identically. Some output formats use JavaScript rendering in the browser:
.md files with no language runtime required.In short: quarto render = code execution (jupyter) + Pandoc
The YAML metadata in the Quarto file dictates the process and how the output is produced.
The most common components of a web page are HTML, CSS, and JavaScript. HTML is relatively simple to learn, but CSS and JavaScript can be much more complicated, depending on how much you want to learn and what you want to do with them.
You don’t need to know much of HTML, CSS, or JavaScript to develop a website using Quarto. If you want to tweak the appearance of your website, you must have some basic knowledge of web development.
HTML = Hyper Text Markup Language. HTML is a standard markup language (not programming language) that provides the primary structure of most websites.
HTML defines the basic structure of a web page. HTML pages are static — the content is not dynamically computed on load.
All elements in HTML are represented by tags. Most HTML tags appear in pairs, e.g., <span style="background-color: #33FF99">highlighted text</span> → highlighted text.
There are a few exceptions, such as the <img> tag, which can be self-closed: <img src="foo.png" />.
An HTML document consists of a head section (metadata, CSS links) and a body section (content).
You can inspect HTML and CSS in your browser: right-click → View Source or Inspect.
Font size and color: <font size="3" color="red">Text here!</font>
Columns: <div class="col2"> ... </div>
Centered text: <center>**Here the text is centered.**</center>
Small image right-aligned: <div align="right"><img src="images/fig.png" width="150px"></div>
Large image centered: <center><img src="images/fig.png" width="500px"></center>
CSS = Cascading Stylesheets. It describes the visual style of HTML documents:
as well as interactive components like drop-down menus, buttons, and forms.
There are 3 ways to define styles:
<style> section in your HTML document<link> in your HTML (most flexible)JavaScript is a programming language (unlike HTML and CSS) that makes web pages interactive and dynamic.
As a data scientist building a Quarto website, you rarely write JavaScript directly. Instead, Python libraries generate it for you:
plotly → outputs a <script> block that renders your chart interactivelyitables → outputs JS that makes your table sortable and searchableYou don’t need to write JS, but it helps to know:
<script> tags you see in your rendered HTML are JavaScriptCmd+Option+J (Mac) or F12 → Console tabCmd+Option+K (Mac) or F12 → Console tabFor learning more: javascript.info is a clear reference.
A dynamic site relies on a server-side language (e.g., PHP, Node.js) to compute and send potentially different content depending on conditions — for example, serving a personalized user profile page fetched from a database.
A static site consists of static files (HTML, CSS, JavaScript, images), and the web server sends exactly the same content to every visitor. It is just one folder of static files.
Quarto has a built-in static site generator (project: type: website), which we will be using. It supports Bootstrap CSS themes as well as custom CSS.
There are many existing static site generators, including Hugo, Jekyll, and Hexo. These provide many site themes, templates, and features, but are more complex.
Quarto’s built-in site generator is a good option if:
.qmd files (what we have been doing all semester!).qmd filesWe render Quarto websites using quarto render from the project directory, or the Render button in VS Code.
VS Code with the Quarto extension provides integrated support for Quarto projects, including preview and build, tied to a _quarto.yml configuration file.
We will use VS Code and the command line to build our websites, and deploy them on GitHub Pages.
There are two main steps for creating a personal website hosted on GitHub:
_quarto.yml fileindex.qmd in your new directory.qmd filesstyles.css) if desiredquarto render in the terminal, orThis creates the output: _site/index.html (or index.html if output-dir: ".")
Let’s break this down, focusing on the essential content.
The minimum requirement for any Quarto website is an index.qmd and a _quarto.yml file.
Let’s walk through a very simple example: a website with two pages (Home and About) and a navigation bar.
First, the configuration file _quarto.yml:
Then two Quarto files, index.qmd:
and about.qmd:
Now let’s discuss the essential _quarto.yml, the index.qmd, and other common elements.
Typically when creating a website, there are various common elements you want to include on all pages (output options, CSS styles, header and footer elements, etc.).
_quarto.ymlA more detailed _quarto.yml:
project:
type: website
output-dir: "."
website:
title: "My Website"
navbar:
left:
- icon: fa-home
href: index.qmd
- text: "About"
href: about.qmd
right:
- text: "External website"
href: https://www.google.com
page-footer:
center: "Copyright 2026"
format:
html:
theme: cosmo
highlight-style: textmate
include-after-body: footer.html
css: styles.css_quarto.ymltitle field under website: sets the site/navbar title.output-dir field indicates which directory to copy site content into ("_site" is the default). Set to "." to keep content alongside source files.format: element defines shared output options for all .qmd documents. Individual documents can override these by specifying their own format: block, which is merged at render time.self-contained: single doc vs. websiteFor a single .qmd document (e.g., a homework), self-contained: true bundles all JS/CSS into one portable HTML file — useful for emailing or submitting.
For a website, Quarto automatically sets self-contained: false. This means:
site_libs/ and shared across all pagesWhy this matters: if you forced self-contained: true on a website, every page would duplicate the same multi-MB libraries — making the site very large. With false, plotly.js is downloaded once by the browser and cached for all your pages.
_quarto.yml: navbarThe navbar element can be used to define a common navigation bar. You can include internal and external links as well as drop-down menus for sites with many pages.
Some capabilities of navigation bars:
dark and light navigation bar colors.left or right..qmd files) and external links._quarto.yml: navbargithub, twitter, house)fa-)When referring to an icon, use its name (e.g., icon: github, or icon: fa-github).
Include a footer via _quarto.yml:
Or via an HTML file referenced as include-after-body: footer.html.
Style sheets: styles.css
As you work on individual pages, you can render them with the Render button (or quarto render index.qmd) just as you would a standalone Quarto document.
Rendering an individual page only renders and previews that page, not the entire site.
To render all pages, use quarto render from the project root, or the Build pane in VS Code.
VS Code with the Quarto extension supports live preview of changes to .qmd files, CSS, and configuration files.
Changes to CSS and YAML config files trigger a refresh of the active page. Changes to .qmd files trigger a rebuild of that page.
Note: After editing shared files or configuration, rebuild the entire site to ensure all pages reflect your changes.
From the terminal, run quarto render to render the entire site, or pass a single file to render just that file:
If you run quarto render from the project directory:
.qmd files in the project root are rendered to HTML._ are not rendered (they are treated as includes/partials)._site by default)._site directory contains a standalone static website ready to deploy.Note: The includes and excludes fields of _quarto.yml can override this behavior.
Quarto makes styling easy. The HTML output includes the Bootstrap framework with multiple themes to choose from.
We can change the website’s theme in _quarto.yml:
Valid built-in themes include: default, cerulean, cosmo, cyborg, darkly, flatly, journal, litera, lumen, lux, materia, minty, morph, pulse, quartz, sandstone, simplex, sketchy, slate, solar, spacelab, superhero, united, vapor, yeti, zephyr.
If you have Python code to share across multiple .qmd documents, put it in a module and import it:
Quarto supports rich interactive Python content:
For many Quarto websites, you will not need to worry about generating HTML output at all — it is created automatically from your Python code.
If you have issues embedding interactive plots, save them as standalone HTML widgets using htmlwidgets:
Then embed via an <iframe>:
To add dynamic, externally-updating content (like a comment box), incorporate a third-party service like Disqus. Disqus provides comment and community capabilities to static websites via JavaScript.
If your website is time-consuming to render, enable caching during development. Use the cache chunk option:
Or enable caching for an entire document in the YAML:
Note: When caching is enabled, figure files are copied rather than moved to _site, since the cache references generated figures.
To clean up generated files, delete the _site directory and any *_cache directories, or use:
Search Engine Optimization (SEO) key points:
title of each page is a key signal to search engines.Your static website is a folder of static files.
All site content is copied to the _site directory (or output-dir). Deployment is simply moving that directory to a web server.
We will focus on hosting websites through GitHub Pages — free and easy to use.
Other popular free options include Netlify and Quarto Pub.
The workflow for deploying to GitHub Pages:
_site folder or use docs/)Quarto Websites — supports multi-page projects, blogs, books, and custom themes.
Hugo — a fast standalone static site generator with many beautiful themes. More complex to set up but highly customizable.
Hugo uses the following project structure:
config.toml — site configurationcontent/ — Markdown pagesstatic/ — images, CSS, JSthemes/ — site themelayouts/ — custom HTML templatesLearn more at gohugo.io.
| Quarto | Hugo | |
|---|---|---|
| Language | Python / R / Julia | Markdown only |
| Code execution | Yes (jupyter) | No |
| Setup | Easy | Moderate |
| Themes | Bootstrap built-in | Huge ecosystem |
| Best for | Data science projects | General websites / blogs |
Static websites (Quarto, GitHub Pages) serve only files — HTML, CSS, JS, images.
Dash and Streamlit apps are Python processes that run a web server. You cannot simply drop them into a _site/ folder.
Options for linking a dashboard to your website:
| Approach | Difficulty | Cost |
|---|---|---|
Embed via <iframe> from a hosted app |
Easy | Free tier available |
| Link to externally hosted app | Easiest | Free tier available |
| Quarto + Observable JS (no server) | Medium | Free |
Step 1 — Deploy your Dash or Streamlit app to a hosting service:
Step 2 — Link or embed from your Quarto website.
requirements.txt and where does it go?requirements.txt is a plain text file that tells Render (or any hosting service) which Python packages to install before running your app.
Location: in the root of your GitHub repository, alongside your app file:
my-repo/
├── dash_app.py ← your app
├── requirements.txt ← dependencies
└── flights_weather.csv ← any data files
Contents — one package per line (for the Dash app from last week):
dash
plotly
pandas
nycflights13
statsmodels
To generate it automatically from your current environment: pip freeze > requirements.txt (then trim to only what the app actually needs).
requirements.txt in the root of your repo (see previous slide)python dash_app.pystreamlit run streamlit_app.py --server.port $PORT --server.address 0.0.0.0https://my-app.onrender.comWhy is it slow? Render’s free tier spins down your service after ~15 minutes of inactivity. The next visit triggers a cold start — Render has to restart the Python process, which takes 30–60 seconds.
This is normal. Once the app is running, it responds instantly.
Tips:
requirements.txt — can take 2–5 minutes for large packages like pandas, plotly, statsmodelsrequirements.txt minimal — only packages your app actually importspandas==2.1.0The easiest option for Streamlit apps:
streamlit_app.py and requirements.txt to a public GitHub repohttps://<your-app>.streamlit.appNo server config needed — Streamlit Community Cloud handles everything automatically.
The simplest approach — add a navbar link in _quarto.yml:
Or add a button link on any page:
<iframe>If you want the dashboard to appear inline on a page, use an <iframe>:
Add this directly in your index.qmd or a dedicated dashboard.qmd page.
Note: Some hosting services block iframe embedding. Render and Streamlit Community Cloud allow it by default.
dashboard.qmd pageThen add to _quarto.yml:
| Dash | Streamlit | |
|---|---|---|
| Free hosting | Render, Railway, HF Spaces | Streamlit Cloud, Render, HF Spaces |
| Deployment effort | Moderate | Very easy (Streamlit Cloud) |
| Customization | High (full HTML/CSS control) | Moderate |
| Best for | Production-grade apps | Rapid prototyping |
For the final project: either works. Streamlit Cloud is the fastest path to a live URL to embed in your website.
These slides are based on:
Dashboards and interactive apps