ウェブサイトを作るシンプルな方法

作品とそれを作るツールが相互干渉的に作られていくような制作過程が好きです。
音楽なら、ある曲の楽器編成や形式、素材、全体を統一するシステムやアルゴリズムは、その曲ごとに考えられるべきです。
同じように、このウェブサイトもその内容に応じて作成するツールを生み出していくように作っていこうと思いました。

そうすると、作品とツール、つまり作られるものと作るものがお互いにフィードバックの関係になります。
作るものが作られるものであり、その逆もまた然り、という関係です。
こうなると作品はツールであり、ツールは作品でもあります。
このような状態になったとき、作り方が作品の一部となり、より興味深い作品の奥行が生まれると考えています。

1 基本原則

このウェブサイトを作るにあたって、以下の基本原則を定めました。

1.1 軽量であること

ウェブサイトはできるだけ軽量であるべきです。
画像や動画、JavaScriptなどのリッチコンテンツは必要最低限に抑え、ページの読み込み速度を最適化します。

1.2 コンテンツが常にWork in progressの状態にあること

このウェブサイトのコンテンツは全て常にWork in progressの状態にあります。
この点が出版物とウェブコンテンツの大きな違いだと考えています。
ウェブコンテンツは本質的に変更が容易であり、常に更新され続ける性質があります。

1.3 1つのHTMLファイルは1つの文書

1つのHTMLファイルが何を表すかについてHTML Living Standardでは明確な定義がありません。
いまのところ、1つのHTMLファイルが1つの文書であるように書かれているべきであると考えています。

1.4 HTML標準に則ったマークアップと最低限のcss

このウェブサイトの基本指針として、HTML仕様に則ったマークアップを行います。
そして、十分にマークアップされていればcssによる装飾は必要最低限でよいと考えています。
HTMLの仕様をハックするような複雑なウェブページはよくありません。

2 ヘッダーフッターの共通化

まずはHeader・Footerを切り出し、Articleと共通パーツを分離。
そしてそれぞれをHTMLテンプレートにインクルードする関数を作りました。

    
def full_html(title_text, header, main_content, footer):
    return f''' 
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="style.css">
        <title>{title_text}</title>
    </head>
    <body>
        {header}
        {main_content}
        {footer}
    </body>
</html>
        '''
    
    

3 titleタグの自動生成

full_html関数のtitle_text引数にtitle_textを渡すことで、titleタグを自動生成します。
title_textはArticleのh1タグから抽出します。
ここでh1タグが1つしかページに存在しないという前提を置いています。

        
def title(html):
    # <h1> と </h1> の間の文字列をすべて抽出
    titleText = re.findall(r'<h1>(.*?)</h1>', html)
    
    return titleText[0]
        
    

4 目次の自動生成

目次はArticle内のh2, h3タグから自動生成します。
目次生成関数tocを作成し、Articleの内容を解析して目次を生成します。

下記のコードはh2タグを抽出する関数と、それを受け取って目次を生成する関数です。

          
def delete_title(html):
    return re.sub(r'<h1>.*?</h1>', '', html, count=1, flags=re.DOTALL)


def heading(html):
    pattern = re.compile(r'<(h[23])>(.*?)</\1>', re.DOTALL)

    result = []
    h2_count = 0
    h3_count = 0
    current_h2 = None

    for m in pattern.finditer(html):
        tag = m.group(1)
        text = m.group(2).strip()

        if tag == "h2":
            h2_count += 1
            h3_count = 0

            current_h2 = {
                "id": f"h2-{h2_count}",
                "tag": "h2",
                "text": text,
                "children": []
            }
            result.append(current_h2)

        elif tag == "h3" and current_h2 is not None:
            h3_count += 1
            current_h2["children"].append({
                "id": f"h2-{h2_count}-h3-{h3_count}",
                "tag": "h3",
                "text": text
            })

    return result
        
    
        
def mkToc(headingList):
    html = ["<ul class='toc'>"]

    for h2 in headingList:
        html.append(
            f'<li><a href="#{h2["id"]}">{h2["text"]}</a>'
        )

        if h2["children"]:
            html.append("<ul>")
            for h3 in h2["children"]:
                html.append(
                    f'<li><a href="#{h3["id"]}">{h3["text"]}</a></li>'
                )
            html.append("</ul>")

        html.append("</li>")

    html.append("</ul>")
    return "\n".join(html)