DEV Community

Cover image for Build your own LinkTree (Taplink) with Python and GitHub Pages
King Triton
King Triton

Posted on • Updated on

Build your own LinkTree (Taplink) with Python and GitHub Pages

How to Create Your Own LinkTree with Python and GitHub Pages

I stumbled upon an article by Lucas Neves Pereira titled "Build your own LinkTree with Go and GitHub Pages". The article describes how to create a LinkTree-like page (Taplink) using Go. As a Python enthusiast, I decided to implement the project in Python. Here’s what I came up with.

my analog of linktree using github page

Step 1: Preparing the Project File Structure

First, let's create the file structure for our project. We’ll organize it to be easily maintainable and deployable on GitHub Pages.

File Structure:

/ (root)
|-- /docs
|   |-- index.html
|   |-- /assets
|       |-- (styles, scripts, icons, etc.)
|-- config.yml
|-- generate_site.py
|-- /themes
Enter fullscreen mode Exit fullscreen mode
  • /docs: This folder will hold the generated HTML files and all necessary assets (images, styles, scripts). This folder will be used for deployment on GitHub Pages.
  • config.yml: Configuration file containing all data for personalizing the site.
  • generate_site.py: Python script that will generate the site based on data from config.yml.
  • /themes: Folder containing themes for the site. In our case, it has a single custom theme that includes HTML templates, styles, scripts, and images.

Step 2: Configuring the Configuration File (config.yml)

The config.yml file contains user data and links that will be displayed on the site. Here’s its content:

name: "King Triton"
picture: "assets/img/picture.jpg"
bio: "Programmer python and php/laravel"
meta:
  lang: "en"
  description: "Programmer python and php/laravel"
  title: "King Triton"
  author: "King Triton"
  siteUrl: "https://king-tri-ton.github.io"
links:
  - name: "Github"
    url: "https://github.com/king-tri-ton"
  - name: "Dev.to"
    url: "https://dev.to/king_triton"
  - name: "Patreon"
    url: "https://www.patreon.com/king_triton"
  - name: "Telegram"
    url: "https://t.me/king_triton"
  - name: "Instagram"
    url: "https://www.instagram.com/king_tri_ton"
theme: "custom"
Enter fullscreen mode Exit fullscreen mode
  • name: The user’s name that will be displayed on the site.
  • picture: Path to the user’s image.
  • bio: A brief biography of the user.
  • meta: Site metadata (language, description, title, author, site URL).
  • links: List of links to be displayed on the site. Each item includes a name and URL.
  • theme: The site theme to use.

Step 3: Developing the Python Script to Generate the Site (generate_site.py)

Next, we’ll write a Python script that will use the theme template, data from config.yml, and generate the HTML file.

import os
import shutil
from jinja2 import Environment, FileSystemLoader
import yaml

# Load configuration
with open('config.yml', 'r') as config_file:
    config = yaml.safe_load(config_file)

# Create output directory
output_dir = 'docs'
os.makedirs(output_dir, exist_ok=True)

# Set up Jinja2
env = Environment(loader=FileSystemLoader('themes/custom'))
template = env.get_template('index.html')

# Generate HTML file
output_html = template.render(config=config)
with open(os.path.join(output_dir, 'index.html'), 'w') as fh:
    fh.write(output_html)

# Copy assets folder to output directory
assets_source = os.path.join('themes', config['theme'], 'assets')
assets_dest = os.path.join(output_dir, 'assets')
if os.path.exists(assets_source):
    shutil.copytree(assets_source, assets_dest, dirs_exist_ok=True)

print("Site generated successfully.")
Enter fullscreen mode Exit fullscreen mode
  • Loading configuration: The script loads data from config.yml.
  • Creating output directory: The docs folder is created automatically if it doesn’t exist.
  • Setting up Jinja2: Jinja2 is used to load the HTML template and render the content.
  • Generating HTML file: The script generates index.html using the configuration data and saves it in the docs folder.
  • Copying assets: All assets (CSS, images, scripts) are copied to the docs/assets folder.

Step 4: Creating the Theme and Assets

Now, create the theme that will be used for the site. In the themes/custom/ folder, there should be the following files:

themes/custom/index.html

This is the main HTML template for the site, using variables from the configuration file.

<!DOCTYPE html>
<html lang="{{ config.meta.lang }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="{{ config.meta.description }}">
    <title>{{ config.meta.title }}</title>
    <meta name="author" content="{{ config.meta.author }}">
    <link rel="canonical" href="{{ config.meta.siteUrl }}">
    <link rel="icon" type="image/x-icon" href="assets/icons/favicon.ico">
    <link rel="stylesheet" href="assets/css/styles.css">
    <meta property="og:title" content="{{ config.meta.title }}">
    <meta property="og:site_name" content="{{ config.meta.title }}">
    <meta property="og:description" content="{{ config.meta.description }}">
    <meta property="og:locale" content="{{ config.meta.lang }}">
    <meta name="twitter:title" content="{{ config.meta.title }}">
    <meta name="twitter:description" content="{{ config.meta.description }}">
</head>
<body>
    <header>
        <img src="{{ config.picture }}" alt="Picture" class="avatar">
        <h1>{{ config.name }}</h1>
        <small class="bio">{{ config.bio }}</small>
    </header>
    <main>
        <section class="links">
            {% for link in config.links %}
            <a class="link-item" href="{{ link.url }}" target="_blank" rel="noopener noreferrer">
                <p>{{ link.name }}</p>
            </a>
            {% endfor %}
        </section>
    </main>
    <footer>
        <small>© <span class="year"></span> {{ config.meta.author }}</small>
    </footer>
    <script src="assets/js/script.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

themes/custom/assets/styles.css

CSS file for styling the page.

/* CSS Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Variables */
:root {
  --max-width: 600px;
  --font-family: 'Inter', sans-serif;
  --padding: 1rem;
  --header-margin-bottom: 1rem;
  --line-height: 2;
  --font-size: 16px;

  --primary-color-light: #ffffff;
  --background-color-light: #f0f0f0;
  --text-color-light: #333;
  --link-color-light: #1a73e8;
  --bio-color-light: #666;

  --primary-color-dark: #1e1e1e;
  --background-color-dark: #121212;
  --text-color-dark: #e0e0e0;
  --link-color-dark: #8ab4f8;
  --bio-color-dark: #aaa;
}

/* Light Theme */
@media (prefers-color-scheme: light) {
  :root {
    --primary-color: var(--primary-color-light);
    --background-color: var(--background-color-light);
    --text-color: var(--text-color-light);
    --link-color: var(--link-color-light);
    --bio-color: var(--bio-color-light);
  }
}

/* Dark Theme */
@media (prefers-color-scheme: dark) {
  :root {
    --primary-color: var(--primary-color-dark);
    --background-color: var(--background-color-dark);
    --text-color: var(--text-color-dark);
    --link-color: var(--link-color-dark);
    --bio-color: var(--bio-color-dark);
  }
}

/* Global Styles */
html {
  font-family: var(--font-family);
  font-size: var(--font-size);
  line-height: var(--line-height);
}

body {
  max-width: var(--max-width);
  min-height: 100vh;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: var(--background-color);
  color: var(--text-color);
  padding: var(--padding);
}

/* Header Styles */
header {
  padding: var(--padding) 0;
  margin-bottom: var(--header-margin-bottom);
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.avatar {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--primary-color);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

h1 {
  font-size: 24px;
  margin-bottom: 0.5rem;
}

.bio {
  font-size: 14px;
  color: var(--bio-color);
  margin-bottom: 1rem;
}

/* Main Content Styles */
main {
  width: 100%;
  flex: 1;
}

.links {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  text-align: center;
  overflow-y: auto;
  max-height: 400px;
}

.link-item {
  display: block;
  padding: 16px 20px;
  text-decoration: none;
  color: var(--link-color);
  background: var(--primary-color);
  border-radius: 12px;
  border: 1px solid var(--link-color);
  transition: background-color 0.25s, color 0.25s;
}

.link-item:hover,
.link-item:focus {
  background-color: var(--link-color);
  color: var(--primary-color);
}

.link-item p {
  line-height: 1.5;
  font-weight: 500;
}

/* Footer Styles */
footer {
  width: 100%;
  text-align: center;
  padding: 1rem 0;
  font-size: 14px;
  gap: 1rem;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* ScrollBar */
::-webkit-scrollbar {
  width: 5px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: transparent;
}

::-webkit-scrollbar-thumb:hover {
  background: transparent;
}
Enter fullscreen mode Exit fullscreen mode

themes/custom/assets/js/script.js

JavaScript file for basic functionalities.

console.log("scripts loaded");

const yearDate = new Date().getFullYear().toString();
document.querySelector(".year").innerText = yearDate;
Enter fullscreen mode Exit fullscreen mode

themes/custom/assets/img/picture.jpg

The photo used as the avatar.

Step 5: Generating the Site

After creating all the files, run the generate_site.py script to generate the site:

python generate_site.py
Enter fullscreen mode Exit fullscreen mode

The site will be generated in the docs folder.

Step 6: Deploying on GitHub Pages

steps to create a github page

  1. Create a new repository on GitHub.
  2. Upload all files, including the docs folder, to the repository.
  3. Go to the repository’s Settings section.
  4. In the Pages section, select the master branch and the /docs folder as the source.
  5. Save changes and wait for GitHub Pages to deploy your site.

Your site will now be available at https://<username>.github.io/<repository-name>/.


And that’s it! You now have your own LinkTree-style site created with Python and deployed on GitHub Pages. You can check the final result at https://king-tri-ton.github.io/pythonpagelink/.

Top comments (2)

Collapse
 
ben profile image
Ben Halpern

Neat

Collapse
 
king_triton profile image
King Triton

thanks