Ereditare lo stile da geoteo.net

Questo repository non definisce un proprio tema: la GitHub-Page generata da Jekyll richiama stile, favicon, configurazione MathJax e rendering Mermaid direttamente da geoteo.net. In questo modo lo stile resta centralizzato e coerente tra tutti i repository che lo adottano, senza duplicare CSS o JS localmente.

1. Cosa fa il tema

2. Passi per replicarlo in un altro repository

2.1 Copiare il layout

Crea _layouts/default.html nella directory di root con il seguente contenuto, effettuerà tutti i richiami a geoteo.net (non serve altro codice di stile):

<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: 'en-US' }}">
    <head>
        <meta charset="UTF-8">
        <script>
            (function () {
                window.applyTheme = function () {
                    var t = localStorage.getItem('theme');
                    if (t) document.documentElement.setAttribute('data-theme', t);
                    var dark = document.documentElement.getAttribute('data-theme') === 'dark';
                    // The query string is load-bearing: browsers cache the tab
                    // favicon per page URL and often won't re-fetch it on reload
                    // just because the icon link's href changed, so each theme needs
                    // a genuinely distinct URL to force a fresh icon.
                    var src = dark
                        ? 'https://geoteo.net/static/favicon-dark.svg?theme=dark'
                        : 'https://geoteo.net/static/favicon.svg?theme=light';
                    var old = document.getElementById('favicon');
                    if (old) old.remove();
                    var icon = document.createElement('link');
                    icon.id = 'favicon';
                    icon.rel = 'icon';
                    icon.type = 'image/svg+xml';
                    icon.href = src;
                    document.head.appendChild(icon);
                };
                applyTheme();
                window.addEventListener('pageshow', function (e) {
                    if (e.persisted) applyTheme();
                });
            })();
        </script>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        {% seo %}
        <link rel="stylesheet" href="https://geoteo.net/static/style.css">
        <script src="https://geoteo.net/static/mathjax-config.js"></script>
        <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
        <script type="module" src="https://geoteo.net/static/mermaid-render.js"></script>
    </head>
    <body>
        <button id="theme-toggle" type="button" class="theme-toggle" aria-label="Toggle dark mode">
            <svg class="icon-moon" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
                <mask id="moon-mask">
                <rect width="24" height="24" fill="white"/>
                <circle cx="15" cy="9" r="7" fill="black"/>
                </mask>
                <circle cx="12" cy="12" r="9" fill="currentColor" mask="url(#moon-mask)"/>
            </svg>
            <svg class="icon-sun" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
                <circle cx="12" cy="12" r="5" fill="currentColor" stroke="none"/>
                <line x1="12" y1="2" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22"/>
                <line x1="4" y1="12" x2="2" y2="12"/><line x1="22" y1="12" x2="20" y2="12"/>
                <line x1="5.6" y1="5.6" x2="4.2" y2="4.2"/><line x1="19.8" y1="19.8" x2="18.4" y2="18.4"/>
                <line x1="5.6" y1="18.4" x2="4.2" y2="19.8"/><line x1="19.8" y1="4.2" x2="18.4" y2="5.6"/>
            </svg>
        </button>
        <div class="container-lg px-3 my-5 markdown-body">
            <main>
                {{ content }}
            </main>
            <p class="gh-footer">
                <a href="{{ site.github.repository_url }}">{{ site.github.repository_name }}</a> powered by <a href="https://geoteo.net">Geoteo</a>
            </p>
        </div>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.0/anchor.min.js" integrity="sha256-lZaRhKri35AyJSypXXs4o6OPFTbTmUoltBbDCbdzegg=" crossorigin="anonymous"></script>
        <script>anchors.add();</script>
        <script>
            (function () {
                document.querySelectorAll('.markdown-body pre').forEach(function (pre) {
                    var code = pre.querySelector('code');
                    if (!code) return;
                    var wrap = document.createElement('div');
                    wrap.className = 'code-block-wrap';
                    pre.parentNode.insertBefore(wrap, pre);
                    wrap.appendChild(pre);
                    var toolbar = document.createElement('div');
                    toolbar.className = 'code-toolbar';
                    var langWrapper = pre.closest('[class*="language-"]');
                    var langMatch = langWrapper && langWrapper.className.match(/language-(\S+)/);
                    if (langMatch && langMatch[1] !== 'plaintext') {
                        var label = document.createElement('span');
                        label.className = 'code-lang';
                        label.textContent = langMatch[1];
                        toolbar.appendChild(label);
                    }
                    var btn = document.createElement('button');
                    btn.type = 'button';
                    btn.className = 'copy-btn';
                    btn.setAttribute('aria-label', 'Copy code');
                    btn.innerHTML =
                        '<svg class="icon-copy" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true">' +
                        '<path fill="currentColor" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/>' +
                        '<path fill="currentColor" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/>' +
                        '</svg>' +
                        '<svg class="icon-check" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true">' +
                        '<path fill="currentColor" d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"/>' +
                        '</svg>';
                    btn.addEventListener('click', function () {
                        navigator.clipboard.writeText(code.textContent).then(function () {
                            btn.classList.add('copied');
                            btn.setAttribute('aria-label', 'Copied!');
                            setTimeout(function () {
                                btn.classList.remove('copied');
                                btn.setAttribute('aria-label', 'Copy code');
                            }, 1500);
                        }).catch(function () {});
                    });
                    toolbar.appendChild(btn);
                    wrap.appendChild(toolbar);
                });
            })();
        </script>
        <script>
            (function () {
                var btn = document.getElementById('theme-toggle');
                btn.addEventListener('click', function () {
                    var next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
                    document.documentElement.setAttribute('data-theme', next);
                    localStorage.setItem('theme', next);
                    window.applyTheme();
                });
            })();
        </script>
    </body>
</html>

2.2 (Opzionale) _config.yml

Se il repo contiene formule matematiche con apici (es. f', f''), aggiungi il file _config.yml nella directory di root con il seguente contenuto:

kramdown:
    # straight quotes required by MathJax for f' f'' etc. inside $...$
    smart_quotes: ["apos", "apos", "quot", "quot"]

Non è necessario per ereditare lo stile grafico, ma evita che Kramdown converta gli apici dritti in apici tipografici dentro $...$.

2.3 Nessun Gemfile o plugin da installare

Il tag {% seo %}, usato nel layout, richiede jekyll-seo-tag che è già incluso di default nella gem github-pages con cui GitHub-Pages compila i siti. Non serve un Gemfile né dichiarare plugin: basta che il repository sia una normale build Jekyll.

2.4 Abilitare GitHub-Pages

Nelle impostazioni del repository: Settings $\to$ Pages $\to$ Build and deployment $\to$ Deploy from a branch, selezionando il branch (es. main) e la cartella root (/). Al primo push, GitHub compila il sito con Jekyll usando il layout copiato al §2.1.

3. Cosa è condiviso con geoteo.net e cosa resta locale

Tutto ciò che riguarda il tema (colori, favicon, toggle) è condiviso; tutto ciò che riguarda il rendering di una pagina markdown vive solo nei repository con GitHub-Pages, perché geoteo.net non ne ha bisogno.

Condiviso con geoteo.net (fa parte del layout/tema base, replicato apposta anche in haunt.scm, generatore della home page):

Non presente su geoteo.net (funzionalità legate al rendering del contenuto Markdown):