GitHub Pages
Documentation Tool
GitHub Pages
Overview
GitHub Pages is a free static site hosting service provided by GitHub. It allows you to deploy HTML, CSS, and JavaScript files directly from GitHub repositories and publish websites through Jekyll's build process.
Details
GitHub Pages was launched by GitHub in 2008 and revolutionized the simplicity of publishing documentation for developers and projects. Based on Jekyll, a static site generator, it automatically generates high-quality websites from Markdown files and HTML/CSS. Content can be version-controlled as Git repositories, enabling collaborative editing through pull requests. It supports custom domains and SSL encryption, allowing you to build professional websites. As of 2025, deep integration with GitHub Actions has enhanced automated builds and deployments. It's available for free with public repositories and can be used with private repositories on paid plans. The Liquid templating language enables dynamic content display, and rich Jekyll plugins provide feature extensions. It supports beautiful code block display with Rouge syntax highlighter, optimized for developer documentation. You can build publication-quality documentation sites from simple Markdown syntax, and it provides change history management and rollback functionality through Git integration.
Advantages and Disadvantages
Advantages
- Completely Free: Unlimited hosting for public repositories
- Git Integration: Version control and collaboration features
- Jekyll Auto-build: Automatic conversion from Markdown to HTML
- Custom Domain: Custom domain support with SSL certificates
- GitHub Actions Integration: Automated deployment and workflows through CI/CD
- Fast Delivery: High performance through static sites
- Security: High security without server-side processing requirements
Disadvantages
- Static Sites Only: No dynamic features or database usage
- Jekyll Dependency: Requires understanding of Ruby-based build system
- Limitations: File size, repository size, and bandwidth restrictions
- Plugin Restrictions: Limited plugin usage for security reasons
- Public Requirement: Free version requires public source code
- Build Time: Increased Jekyll build time for large sites
Key Links
- GitHub Pages Official Site
- GitHub Pages Documentation
- Jekyll Official Site
- Jekyll on GitHub Pages
- GitHub Actions for Pages
- GitHub Pages Examples
Usage Examples
Repository Setup and Initialization
# Create and clone new repository
git clone https://github.com/username/username.github.io.git
cd username.github.io
# Initialize Jekyll site
echo "Hello GitHub Pages!" > index.html
git add .
git commit -m "Initial commit"
git push origin main
# Repository Pages site structure example
username.github.io/
├── _config.yml # Jekyll configuration file
├── _layouts/ # Layout templates
│ ├── default.html
│ ├── post.html
│ └── page.html
├── _includes/ # Reusable partials
│ ├── header.html
│ ├── footer.html
│ └── navigation.html
├── _posts/ # Blog posts
│ └── 2025-06-20-welcome.md
├── _data/ # Data files
│ └── navigation.yml
├── assets/ # CSS, JS, images
│ ├── css/
│ ├── js/
│ └── images/
├── docs/ # Documentation pages
├── index.md # Home page
└── README.md
Jekyll Configuration File (_config.yml)
# _config.yml - GitHub Pages Jekyll configuration
title: "Project Documentation"
description: "Comprehensive documentation for technical project"
baseurl: "" # Set only for subdirectory
url: "https://username.github.io"
author: "Developer Name"
email: "[email protected]"
# Build settings
markdown: kramdown
highlighter: rouge
theme: minima
# Jekyll plugins (GitHub Pages compatible)
plugins:
- jekyll-feed
- jekyll-sitemap
- jekyll-seo-tag
- jekyll-redirect-from
- jekyll-mentions
- jekyll-relative-links
- jekyll-optional-front-matter
- jekyll-readme-index
- jekyll-default-layout
- jekyll-titles-from-headings
# Kramdown configuration
kramdown:
input: GFM
syntax_highlighter: rouge
syntax_highlighter_opts:
css_class: 'highlight'
span:
line_numbers: false
block:
line_numbers: true
# Collection configuration
collections:
docs:
output: true
permalink: /:collection/:name/
api:
output: true
permalink: /api/:name/
# Default front matter
defaults:
- scope:
path: ""
type: "posts"
values:
layout: "post"
comments: true
- scope:
path: ""
type: "docs"
values:
layout: "page"
- scope:
path: ""
type: "api"
values:
layout: "api"
# Exclude from processing
exclude:
- Gemfile
- Gemfile.lock
- node_modules
- vendor/
- .sass-cache/
- .jekyll-cache/
- gemfiles/
# Navigation configuration
navigation:
- title: Home
url: /
- title: Documentation
url: /docs/
- title: API
url: /api/
- title: Blog
url: /blog/
- title: About
url: /about/
# SEO and social
twitter:
username: username
card: summary
facebook:
app_id: 1234567890
publisher: 1234567890
logo: /assets/images/logo.png
social:
name: Project Name
links:
- https://twitter.com/username
- https://github.com/username
- https://linkedin.com/in/username
# Analytics
google_analytics: G-XXXXXXXXXX
# Comments (GitHub Issues)
github:
repository_url: https://github.com/username/project
Layout Template Creation
<!-- _layouts/default.html -->
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: 'en' }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{%- seo -%}
<link rel="stylesheet" href="{{ '/assets/css/style.css' | relative_url }}">
<link rel="shortcut icon" type="image/png" href="{{ '/assets/images/favicon.png' | relative_url }}">
{%- feed_meta -%}
{%- if jekyll.environment == 'production' and site.google_analytics -%}
{%- include google-analytics.html -%}
{%- endif -%}
</head>
<body>
{%- include header.html -%}
<main class="page-content" aria-label="Content">
<div class="wrapper">
{{ content }}
</div>
</main>
{%- include footer.html -%}
<script src="{{ '/assets/js/main.js' | relative_url }}"></script>
</body>
</html>
<!-- _layouts/post.html -->
---
layout: default
---
<article class="post h-entry" itemscope itemtype="http://schema.org/BlogPosting">
<header class="post-header">
<h1 class="post-title p-name" itemprop="name headline">{{ page.title | escape }}</h1>
<p class="post-meta">
<time class="dt-published" datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">
{%- assign date_format = site.minima.date_format | default: "%b %-d, %Y" -%}
{{ page.date | date: date_format }}
</time>
{%- if page.modified_date -%}
~
<time class="dt-modified" datetime="{{ page.modified_date | date_to_xmlschema }}" itemprop="dateModified">
Updated: {{ page.modified_date | date: date_format }}
</time>
{%- endif -%}
{%- if page.author -%}
• {% for author in page.author %}
<span itemprop="author" itemscope itemtype="http://schema.org/Person">
<span class="p-author h-card" itemprop="name">{{ author }}</span>
</span>
{%- if forloop.last == false %}, {% endif -%}
{% endfor %}
{%- endif -%}
</p>
</header>
<div class="post-content e-content" itemprop="articleBody">
{{ content }}
</div>
{%- if site.disqus.shortname -%}
{%- include disqus_comments.html -%}
{%- endif -%}
<a class="u-url" href="{{ page.url | relative_url }}" hidden></a>
</article>
Navigation and Header
<!-- _includes/header.html -->
<header class="site-header" role="banner">
<div class="wrapper">
{%- assign default_paths = site.pages | map: "path" -%}
{%- assign page_paths = site.header_pages | default: default_paths -%}
<a class="site-title" rel="author" href="{{ "/" | relative_url }}">
<img src="{{ '/assets/images/logo.png' | relative_url }}" alt="{{ site.title }}" class="site-logo">
{{ site.title | escape }}
</a>
{%- if page_paths -%}
<nav class="site-nav">
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger">
<span class="menu-icon">
<svg viewBox="0 0 18 15" width="18px" height="15px">
<path d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.032C17.335,0,18,0.665,18,1.484L18,1.484z M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.032C17.335,6.031,18,6.696,18,7.516L18,7.516z M18,13.516C18,14.335,17.335,15,16.516,15H1.484 C0.665,15,0,14.335,0,13.516l0,0c0-0.82,0.665-1.483,1.484-1.483h15.032C17.335,12.031,18,12.695,18,13.516L18,13.516z"/>
</svg>
</span>
</label>
<div class="trigger">
<a class="page-link" href="{{ '/' | relative_url }}">Home</a>
{%- for path in page_paths -%}
{%- assign my_page = site.pages | where: "path", path | first -%}
{%- if my_page.title -%}
<a class="page-link" href="{{ my_page.url | relative_url }}">{{ my_page.title | escape }}</a>
{%- endif -%}
{%- endfor -%}
<!-- Dropdown menu -->
<div class="dropdown">
<button class="dropbtn">Documentation</button>
<div class="dropdown-content">
<a href="{{ '/docs/getting-started/' | relative_url }}">Getting Started</a>
<a href="{{ '/docs/installation/' | relative_url }}">Installation</a>
<a href="{{ '/docs/configuration/' | relative_url }}">Configuration</a>
<a href="{{ '/docs/api/' | relative_url }}">API Reference</a>
</div>
</div>
</div>
</nav>
{%- endif -%}
</div>
</header>
GitHub Actions Auto-deployment Configuration
# .github/workflows/pages.yml
name: Build and Deploy to GitHub Pages
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# GitHub token permissions
permissions:
contents: read
pages: write
id-token: write
# Concurrency to prevent multiple deployments
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true
cache-version: 0
- name: Setup Pages
id: pages
uses: actions/configure-pages@v3
- name: Build with Jekyll
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
env:
JEKYLL_ENV: production
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
Custom Domain Configuration
<!-- CNAME file for custom domain -->
docs.example.com
# _config.yml - Additional custom domain configuration
url: "https://docs.example.com"
enforce_ssl: true
# Custom domain redirect settings
plugins:
- jekyll-redirect-from
# Redirect rules
redirect_from:
- /old-url/
- /another-old-url/
redirect_to: https://docs.example.com/new-location/
Markdown Content Creation
---
layout: post
title: "Getting Started with Project Documentation"
date: 2025-06-20 10:00:00 +0900
categories: [documentation, tutorial]
tags: [github-pages, jekyll, markdown]
author: "Developer"
description: "How to create project documentation with GitHub Pages"
image: "/assets/images/documentation-guide.png"
toc: true
---
# Getting Started with Project Documentation
Learn how to create project documentation using GitHub Pages.
## Table of Contents
{:.no_toc}
* toc
{:toc}
## Introduction
GitHub Pages is a powerful tool for easily publishing project documentation.
### Required Knowledge
- Basic Git usage
- Markdown syntax
- Basic HTML/CSS (for customization)
## Project Setup
### 1. Repository Preparation
```bash
# Clone repository
git clone https://github.com/username/project.git
cd project
# Create docs directory (for project documentation)
mkdir docs
cd docs
2. Jekyll Initialization
# Create Gemfile
cat > Gemfile << EOF
source "https://rubygems.org"
gem "github-pages", group: :jekyll_plugins
gem "jekyll-feed"
gem "jekyll-sitemap"
gem "jekyll-seo-tag"
EOF
# Install dependencies
bundle install
# Initialize Jekyll site
bundle exec jekyll new . --force
Content Creation
Utilizing Markdown
## API Reference
### `getUserData(userId)`
API function to retrieve user data.
**Parameters:**
- `userId` (string): User ID
**Returns:**
- Promise<UserData>: Promise of user data
**Usage:**
```javascript
const userData = await getUserData('user123');
console.log(userData.name);
Response Example:
{
"id": "user123",
"name": "John Doe",
"email": "[email protected]",
"created_at": "2025-01-01T00:00:00Z"
}
### Enhanced Code Blocks
```markdown
## Syntax Highlighting
```python
def calculate_fibonacci(n):
"""Function to calculate Fibonacci sequence"""
if n <= 1:
return n
return calculate_fibonacci(n-1) + calculate_fibonacci(n-2)
# Usage example
for i in range(10):
print(f"F({i}) = {calculate_fibonacci(i)}")
# _config.yml configuration example
title: "Project Name"
description: "Project description"
highlighter: rouge
markdown: kramdown
kramdown:
syntax_highlighter: rouge
syntax_highlighter_opts:
css_class: 'highlight'
line_numbers: true
## Utilizing Includes and Layouts
```liquid
<!-- API endpoint documentation include -->
<!-- _includes/api-endpoint.html -->
<div class="api-endpoint">
<div class="method {{ include.method | downcase }}">{{ include.method }}</div>
<div class="url">{{ include.url }}</div>
<div class="description">{{ include.description }}</div>
</div>
<!-- Usage example -->
{% include api-endpoint.html method="GET" url="/api/users" description="Retrieve user list" %}
{% include api-endpoint.html method="POST" url="/api/users" description="Create new user" %}
Search Function Implementation
// assets/js/search.js
class SimpleSearch {
constructor() {
this.searchInput = document.getElementById('search-input');
this.searchResults = document.getElementById('search-results');
this.searchData = [];
this.loadSearchData();
this.bindEvents();
}
async loadSearchData() {
try {
const response = await fetch('/search.json');
this.searchData = await response.json();
} catch (error) {
console.error('Search data loading failed:', error);
}
}
bindEvents() {
this.searchInput.addEventListener('input', (e) => {
this.performSearch(e.target.value);
});
}
performSearch(query) {
if (query.length < 2) {
this.searchResults.innerHTML = '';
return;
}
const results = this.searchData.filter(item =>
item.title.toLowerCase().includes(query.toLowerCase()) ||
item.content.toLowerCase().includes(query.toLowerCase())
);
this.displayResults(results);
}
displayResults(results) {
if (results.length === 0) {
this.searchResults.innerHTML = '<p>No search results found.</p>';
return;
}
const html = results.map(result => `
<div class="search-result">
<h3><a href="${result.url}">${result.title}</a></h3>
<p>${this.truncateText(result.content, 150)}</p>
</div>
`).join('');
this.searchResults.innerHTML = html;
}
truncateText(text, length) {
return text.length > length ? text.substring(0, length) + '...' : text;
}
}
// Initialize
document.addEventListener('DOMContentLoaded', () => {
new SimpleSearch();
});
Development and Deployment Commands
# Start local development server
bundle exec jekyll serve --port 4000 --livereload
# Production build
JEKYLL_ENV=production bundle exec jekyll build
# Deploy (for GitHub Pages)
git add .
git commit -m "Update documentation"
git push origin main
# Check with custom domain
curl -I https://docs.example.com
# Check SSL configuration
openssl s_client -connect docs.example.com:443 -servername docs.example.com
# Debug build issues
bundle exec jekyll build --verbose
bundle exec jekyll doctor
# Update dependencies
bundle update
bundle outdated
# Clear cache
bundle exec jekyll clean
rm -rf _site .jekyll-cache