Theme Switching and Navigation Logic Documentation
This is an internal system design — you should fully understand how your _config.yml, front matter, layout files, and navigation interact depending on the theme you choose.
Table of Contents
- Theme Declaration (
_config.yml) - Front Matter Layout Declaration (
layout: base) base.htmlLogic: Theme Detection- Theme Layout Includes
minimal_default.htmlminima_default.html
- Navigation System
- Navigation in Minimal
- Navigation in Minima
- Styling
- TailwindCSS for Minimal
- SASS Skins for Minima
- Important Notes
1. Theme Declaration (_config.yml)
Your theme is declared in _config.yml at the root of the repository.
To use the Minimal theme:
remote_theme: opencodingsociety/minimal@v0.2.0
To use the Minima theme:
remote_theme: jekyll/minima
This remote theme declaration determines which CSS/JS, layout includes, and navigation logic will be used.
2. Front Matter Layout Declaration (layout: base)
In every page or post, you should declare the base layout in the front matter:
layout: base
This ensures that base.html will control the dynamic switching logic.
Example full front matter:
---
layout: base
title: My Post Title
nav: true
---
3. base.html Logic: Theme Detection
Here is the logic in base.html:
<!DOCTYPE html>
<html lang="en">
<!DOCTYPE html>
<html lang="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"><!-- Begin Jekyll SEO tag v2.8.0 -->
<title>Theme Switching and Navigation Logic Documentation | Open Coding Society</title>
<meta name="generator" content="Jekyll v3.10.0" />
<meta property="og:title" content="Theme Switching and Navigation Logic Documentation" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="Class of 2026" />
<meta property="og:description" content="Class of 2026" />
<link rel="canonical" href="https://open-coding-society.github.io/student_2026/theme_and_nav_docs" />
<meta property="og:url" content="https://open-coding-society.github.io/student_2026/theme_and_nav_docs" />
<meta property="og:site_name" content="Open Coding Society" />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="Theme Switching and Navigation Logic Documentation" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"WebPage","description":"Class of 2026","headline":"Theme Switching and Navigation Logic Documentation","url":"https://open-coding-society.github.io/student_2026/theme_and_nav_docs"}</script>
<!-- End Jekyll SEO tag -->
<link id="main-stylesheet" rel="stylesheet" href="/student_2026/assets/css/style.css"><link type="application/atom+xml" rel="alternate" href="https://open-coding-society.github.io/student_2026/feed.xml" title="Open Coding Society" />
</head>
<body><header class="site-header">
<div class="wrapper">
<a class="site-title" rel="author" href="/student_2026/">Open Coding Society</a>
<nav class="site-nav">
<input type="checkbox" id="nav-trigger" />
<label for="nav-trigger">
<span class="menu-icon"></span>
</label>
<div class="nav-items">
<a class="nav-item" href="/student_2026/tools/lessons">Lessons</a>
<a class="nav-item" href="/student_2026/blogs/">Blogs</a>
<a class="nav-item" href="/student_2026/search/">Search</a>
<a class="nav-item" href="/student_2026/documents/">Docs</a>
<a class="nav-item" href="/student_2026/leaderboard/">Leaderboard</a>
<a class="nav-item" href="/student_2026/resource/">Resources</a>
<a class="nav-item" href="/student_2026/login">Login</a>
<a class="nav-item" href="/student_2026/logout">Logout</a>
</div>
</nav>
</div>
</header>
<main class="page-content" aria-label="Content">
<div class="wrapper"><h1 class="page-title">Theme Switching and Navigation Logic Documentation</h1><div class="post-meta-block">
<ul class="inline_toc">
<ul><li><a href="#2>Snake">Snake</a>
</ul><!-- Comments powered by Utterances --></div>
<style>
body{
}
.wrap{
margin-left: auto;
margin-right: auto;
}
canvas{
display: none;
border-style: solid;
border-width: 10px;
border-color: #FFFFFF;
}
canvas:focus{
outline: none;
}
/* All screens style */
#gameover p, #setting p, #menu p{
font-size: 20px;
}
#gameover a, #setting a, #menu a{
font-size: 30px;
display: block;
}
#gameover a:hover, #setting a:hover, #menu a:hover{
cursor: pointer;
}
#gameover a:hover::before, #setting a:hover::before, #menu a:hover::before{
content: ">";
margin-right: 10px;
}
#menu{
display: block;
}
#gameover{
display: none;
}
#setting{
display: none;
}
#setting input{
display:none;
}
#setting label{
cursor: pointer;
}
#setting input:checked + label{
background-color: #FFF;
color: #000;
}
</style>
<h2>Snake</h2>
<div class="container">
<p class="fs-4">Score: <span id="score_value">0</span></p>
<div class="container bg-secondary" style="text-align:center;">
<!-- Main Menu -->
<div id="menu" class="py-4 text-light">
<p>Welcome to Snake, press <span style="background-color: #FFFFFF; color: #000000">space</span> to begin</p>
<a id="new_game" class="link-alert">new game</a>
<a id="setting_menu" class="link-alert">settings</a>
</div>
<!-- Game Over -->
<div id="gameover" class="py-4 text-light">
<p>Game Over, press <span style="background-color: #FFFFFF; color: #000000">space</span> to try again</p>
<a id="new_game1" class="link-alert">new game</a>
<a id="setting_menu1" class="link-alert">settings</a>
</div>
<!-- Play Screen -->
<canvas id="snake" class="wrap" width="320" height="320" tabindex="1"></canvas>
<!-- Settings Screen -->
<div id="setting" class="py-4 text-light">
<p>Settings Screen, press <span style="background-color: #FFFFFF; color: #000000">space</span> to go back to playing</p>
<a id="new_game2" class="link-alert">new game</a>
<br />
<p>Speed:
<input id="speed1" type="radio" name="speed" value="120" checked="" />
<label for="speed1">Slow</label>
<input id="speed2" type="radio" name="speed" value="75" />
<label for="speed2">Normal</label>
<input id="speed3" type="radio" name="speed" value="35" />
<label for="speed3">Fast</label>
</p>
<p>Wall:
<input id="wallon" type="radio" name="wall" value="1" checked="" />
<label for="wallon">On</label>
<input id="walloff" type="radio" name="wall" value="0" />
<label for="walloff">Off</label>
</p>
</div>
</div>
</div>
<script>
(function(){
/* Attributes of Game */
/////////////////////////////////////////////////////////////
// Canvas & Context
const canvas = document.getElementById("snake");
const ctx = canvas.getContext("2d");
// HTML Game IDs
const SCREEN_SNAKE = 0;
const screen_snake = document.getElementById("snake");
const ele_score = document.getElementById("score_value");
const speed_setting = document.getElementsByName("speed");
const wall_setting = document.getElementsByName("wall");
// HTML Screen IDs (div)
const SCREEN_MENU = -1, SCREEN_GAME_OVER=1, SCREEN_SETTING=2;
const screen_menu = document.getElementById("menu");
const screen_game_over = document.getElementById("gameover");
const screen_setting = document.getElementById("setting");
// HTML Event IDs (a tags)
const button_new_game = document.getElementById("new_game");
const button_new_game1 = document.getElementById("new_game1");
const button_new_game2 = document.getElementById("new_game2");
const button_setting_menu = document.getElementById("setting_menu");
const button_setting_menu1 = document.getElementById("setting_menu1");
// Game Control
const BLOCK = 10; // size of block rendering
let SCREEN = SCREEN_MENU;
let snake;
let snake_dir;
let snake_next_dir;
let snake_speed;
let food = {x: 0, y: 0};
let score;
let wall;
/* Display Control */
/////////////////////////////////////////////////////////////
// 0 for the game
// 1 for the main menu
// 2 for the settings screen
// 3 for the game over screen
let showScreen = function(screen_opt){
SCREEN = screen_opt;
switch(screen_opt){
case SCREEN_SNAKE:
screen_snake.style.display = "block";
screen_menu.style.display = "none";
screen_setting.style.display = "none";
screen_game_over.style.display = "none";
break;
case SCREEN_GAME_OVER:
screen_snake.style.display = "block";
screen_menu.style.display = "none";
screen_setting.style.display = "none";
screen_game_over.style.display = "block";
break;
case SCREEN_SETTING:
screen_snake.style.display = "none";
screen_menu.style.display = "none";
screen_setting.style.display = "block";
screen_game_over.style.display = "none";
break;
}
}
/* Actions and Events */
/////////////////////////////////////////////////////////////
window.onload = function(){
// HTML Events to Functions
button_new_game.onclick = function(){newGame();};
button_new_game1.onclick = function(){newGame();};
button_new_game2.onclick = function(){newGame();};
button_setting_menu.onclick = function(){showScreen(SCREEN_SETTING);};
button_setting_menu1.onclick = function(){showScreen(SCREEN_SETTING);};
// speed
setSnakeSpeed(150);
for(let i = 0; i < speed_setting.length; i++){
speed_setting[i].addEventListener("click", function(){
for(let i = 0; i < speed_setting.length; i++){
if(speed_setting[i].checked){
setSnakeSpeed(speed_setting[i].value);
}
}
});
}
// wall setting
setWall(1);
for(let i = 0; i < wall_setting.length; i++){
wall_setting[i].addEventListener("click", function(){
for(let i = 0; i < wall_setting.length; i++){
if(wall_setting[i].checked){
setWall(wall_setting[i].value);
}
}
});
}
// activate window events
window.addEventListener("keydown", function(evt) {
// spacebar detected
if(evt.code === "Space" && SCREEN !== SCREEN_SNAKE)
newGame();
}, true);
}
/* Snake is on the Go (Driver Function) */
/////////////////////////////////////////////////////////////
let mainLoop = function(){
let _x = snake[0].x;
let _y = snake[0].y;
snake_dir = snake_next_dir; // read async event key
// Direction 0 - Up, 1 - Right, 2 - Down, 3 - Left
switch(snake_dir){
case 0: _y--; break;
case 1: _x++; break;
case 2: _y++; break;
case 3: _x--; break;
}
snake.pop(); // tail is removed
snake.unshift({x: _x, y: _y}); // head is new in new position/orientation
// Wall Checker
if(wall === 1){
// Wall on, Game over test
if (snake[0].x < 0 || snake[0].x === canvas.width / BLOCK || snake[0].y < 0 || snake[0].y === canvas.height / BLOCK){
showScreen(SCREEN_GAME_OVER);
return;
}
}else{
// Wall Off, Circle around
for(let i = 0, x = snake.length; i < x; i++){
if(snake[i].x < 0){
snake[i].x = snake[i].x + (canvas.width / BLOCK);
}
if(snake[i].x === canvas.width / BLOCK){
snake[i].x = snake[i].x - (canvas.width / BLOCK);
}
if(snake[i].y < 0){
snake[i].y = snake[i].y + (canvas.height / BLOCK);
}
if(snake[i].y === canvas.height / BLOCK){
snake[i].y = snake[i].y - (canvas.height / BLOCK);
}
}
}
// Snake vs Snake checker
for(let i = 1; i < snake.length; i++){
// Game over test
if (snake[0].x === snake[i].x && snake[0].y === snake[i].y){
showScreen(SCREEN_GAME_OVER);
return;
}
}
// Snake eats food checker
if(checkBlock(snake[0].x, snake[0].y, food.x, food.y)){
snake[snake.length] = {x: snake[0].x, y: snake[0].y};
altScore(++score);
addFood();
activeDot(food.x, food.y);
}
// Repaint canvas
ctx.beginPath();
ctx.fillStyle = "royalblue";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Paint snake
for(let i = 0; i < snake.length; i++){
activeDot(snake[i].x, snake[i].y);
}
// Paint food
activeDot(food.x, food.y);
// Debug
//document.getElementById("debug").innerHTML = snake_dir + " " + snake_next_dir + " " + snake[0].x + " " + snake[0].y;
// Recursive call after speed delay, déjà vu
setTimeout(mainLoop, snake_speed);
}
/* New Game setup */
/////////////////////////////////////////////////////////////
let newGame = function(){
// snake game screen
showScreen(SCREEN_SNAKE);
screen_snake.focus();
// game score to zero
score = 0;
altScore(score);
// initial snake
snake = [];
snake.push({x: 0, y: 15});
snake_next_dir = 1;
// food on canvas
addFood();
// activate canvas event
canvas.onkeydown = function(evt) {
changeDir(evt.keyCode);
}
mainLoop();
}
/* Key Inputs and Actions */
/////////////////////////////////////////////////////////////
let changeDir = function(key){
// test key and switch direction
switch(key) {
case 37: // left arrow
if (snake_dir !== 1) // not right
snake_next_dir = 3; // then switch left
break;
case 38: // up arrow
if (snake_dir !== 2) // not down
snake_next_dir = 0; // then switch up
break;
case 39: // right arrow
if (snake_dir !== 3) // not left
snake_next_dir = 1; // then switch right
break;
case 40: // down arrow
if (snake_dir !== 0) // not up
snake_next_dir = 2; // then switch down
break;
}
}
/* Dot for Food or Snake part */
/////////////////////////////////////////////////////////////
let activeDot = function(x, y){
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(x * BLOCK, y * BLOCK, BLOCK, BLOCK);
}
/* Random food placement */
/////////////////////////////////////////////////////////////
let addFood = function(){
food.x = Math.floor(Math.random() * ((canvas.width / BLOCK) - 1));
food.y = Math.floor(Math.random() * ((canvas.height / BLOCK) - 1));
for(let i = 0; i < snake.length; i++){
if(checkBlock(food.x, food.y, snake[i].x, snake[i].y)){
addFood();
}
}
}
/* Collision Detection */
/////////////////////////////////////////////////////////////
let checkBlock = function(x, y, _x, _y){
return (x === _x && y === _y);
}
/* Update Score */
/////////////////////////////////////////////////////////////
let altScore = function(score_val){
ele_score.innerHTML = String(score_val);
}
/////////////////////////////////////////////////////////////
// Change the snake speed...
// 150 = slow
// 100 = normal
// 50 = fast
let setSnakeSpeed = function(speed_value){
snake_speed = speed_value;
}
/////////////////////////////////////////////////////////////
let setWall = function(wall_value){
wall = wall_value;
if(wall === 0){screen_snake.style.borderColor = "#606060";}
if(wall === 1){screen_snake.style.borderColor = "#FFFFFF";}
}
})();
</script>
</div>
</main><link id="fa-stylesheet" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css">
<footer class="site-footer h-card">
<data class="u-url" value="/student_2026/"></data>
<div class="wrapper">
<div class="footer-col-wrapper">
<div class="footer-col">
</div>
<div class="footer-col">
<p>Class of 2026</p>
</div>
</div>
<div class="social-links"><ul class="social-media-list"><li>
<a rel="me" href="" target="_blank" title="">
<span class="grey fa-brands fa- fa-lg"></span>
</a>
</li><li>
<a rel="me" href="" target="_blank" title="">
<span class="grey fa-brands fa- fa-lg"></span>
</a>
</li><li>
<a rel="me" href="" target="_blank" title="">
<span class="grey fa-brands fa- fa-lg"></span>
</a>
</li>
<li>
<a href="https://open-coding-society.github.io/student_2026/feed.xml" target="_blank" title="Subscribe to syndication feed">
<svg class="svg-icon grey" viewbox="0 0 16 16">
<path d="M12.8 16C12.8 8.978 7.022 3.2 0 3.2V0c8.777 0 16 7.223 16 16h-3.2zM2.194
11.61c1.21 0 2.195.985 2.195 2.196 0 1.21-.99 2.194-2.2 2.194C.98 16 0 15.017 0
13.806c0-1.21.983-2.195 2.194-2.195zM10.606
16h-3.11c0-4.113-3.383-7.497-7.496-7.497v-3.11c5.818 0 10.606 4.79 10.606 10.607z"
/>
</svg>
</a>
</li>
</ul>
</div>
</div>
</footer>
</body>
</html>
</html>
What This Does:
- If
remote_themecontainsminimal@v0.2.0, it includesminimal_default.html. - If
remote_themecontainsminima, it includesminima_default.html.
The base.html is acting like a smart router that switches the layout depending on your theme.
4. Theme Layout Includes
minimal_default.html
This layout is structured for Tailwind CSS based design — simple, clean, and flexible.
It uses Tailwind utility classes to handle spacing, sidebar, and responsive behavior.
It also defines a custom navigation logic (explained below).
minima_default.html
This layout uses the standard Minima theme structure.
It inherits SASS styling and includes default header, footer, and content areas as provided by Minima.
The navigation and structure are entirely based on Jekyll’s default Minima logic.
5. Navigation System
The navigation bar is different depending on the theme.
Navigation in Minimal
Minimal uses front matter to determine navigation links. Pages must explicitly opt-in to navigation.
Here is the logic:
<!-- NAVIGATION -->
<nav class="mb-10 border-b pb-4 flex flex-col gap-2 text-sm text-blue-600">
<a href="/student_2026/blogs/" class="hover:underline">Blogs</a>
<a href="/student_2026/documents/" class="hover:underline">Docs</a>
<a href="/student_2026/github/readme" class="hover:underline">Readme</a>
<a href="/student_2026/readme/" class="hover:underline">Readme</a>
<a href="/student_2026/leaderboard/" class="hover:underline">Leaderboard</a>
<a href="/student_2026/resource/" class="hover:underline">Resources</a>
<a href="/student_2026/search/" class="hover:underline">Search</a>
</nav>
How to Add a Page to the Navigation Bar in Minimal
In your page’s front matter, add:
nav: true
This opt-in system gives you full control over what shows up in the sidebar.
Navigation in Minima
Minima uses the _config.yml file to configure the navigation:
# Example _config.yml
header_pages:
- about.md
- contact.md
- blog.md
You list the page filenames under header_pages, and they will be included in the top navbar automatically.
There is no need for nav: true front matter in Minima — the system is entirely controlled by _config.yml.
6. Styling
TailwindCSS for Minimal
When using the Minimal theme:
- TailwindCSS (via CDN) is loaded in the
<head>to provide utility classes for layout. - Only layout is controlled by Tailwind. Markdown typography is preserved via the original CSS (
style.cssfrom Minimal or custom).
No Tailwind Typography plugin is used, to prevent interference with Markdown.
SASS Skins for Minima
When using the Minima theme:
- SASS customization is supported via
_sass/minima/custom-styles.scss. - You can modify or extend Minima’s appearance by editing this SASS file.
Example snippet in custom-styles.scss:
// Custom color overrides
$primary-color: #1a202c;
$link-color: #3182ce;
Changes here allow you to apply a custom “skin” to Minima without disrupting its structure.
7. Important Notes
- Pages using
layout: basemust rely on theremote_themevalue to determine final rendering. - Minimal is designed for simplicity and modern layouts with Tailwind.
- Minima is designed for traditional blog structures with SASS theming.
- Markdown styling is preserved under Minimal by not using Tailwind Typography plugin.
- Navigation must be properly configured depending on the theme:
- Minimal: via
nav: truein front matter. - Minima: via
header_pagesin_config.yml.
- Minimal: via
- Issue templates and PR templates are available under
.github/to guide contributions.