@terrymooreii/sia 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,987 @@
1
+ # Creating Themes
2
+
3
+ This guide explains how to create custom themes for Sia, distribute them as npm packages, and customize existing themes.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Theme Overview](#theme-overview)
8
+ - [Theme Structure](#theme-structure)
9
+ - [Required Files](#required-files)
10
+ - [Layouts](#layouts)
11
+ - [Pages](#pages)
12
+ - [Includes](#includes)
13
+ - [Styles](#styles)
14
+ - [Shared Includes](#shared-includes)
15
+ - [Dark Mode Support](#dark-mode-support)
16
+ - [External Theme Packages](#external-theme-packages)
17
+ - [Creating a Theme Package](#creating-a-theme-package)
18
+ - [Publishing Your Theme](#publishing-your-theme)
19
+ - [Customizing Existing Themes](#customizing-existing-themes)
20
+ - [Built-in Themes](#built-in-themes)
21
+ - [Best Practices](#best-practices)
22
+
23
+ ---
24
+
25
+ ## Theme Overview
26
+
27
+ Sia themes control the visual appearance and structure of your site. A theme consists of:
28
+
29
+ - **Layouts** - Base templates that wrap content (post, page, note layouts)
30
+ - **Pages** - Templates for listing pages (homepage, blog, tags)
31
+ - **Includes** - Reusable components (header, footer, pagination)
32
+ - **Styles** - CSS files for styling
33
+
34
+ ### Selecting a Theme
35
+
36
+ Set your theme in `_config.yml`:
37
+
38
+ ```yaml
39
+ theme:
40
+ name: minimal # Options: main, minimal, developer, magazine
41
+ ```
42
+
43
+ The theme configuration is an object that allows for additional theme-specific options in the future.
44
+
45
+ ---
46
+
47
+ ## Theme Structure
48
+
49
+ A complete theme follows this directory structure:
50
+
51
+ ```
52
+ themes/your-theme/
53
+ ├── layouts/
54
+ │ ├── base.njk # Base HTML structure
55
+ │ ├── post.njk # Blog post layout
56
+ │ ├── page.njk # Static page layout
57
+ │ └── note.njk # Note layout
58
+ ├── includes/
59
+ │ ├── header.njk # Site header/navigation
60
+ │ ├── footer.njk # Site footer
61
+ │ ├── hero.njk # Homepage hero section
62
+ │ ├── pagination.njk # Pagination component
63
+ │ └── tag-list.njk # Tag cloud/list component
64
+ ├── pages/
65
+ │ ├── index.njk # Homepage
66
+ │ ├── blog.njk # Blog listing
67
+ │ ├── notes.njk # Notes listing
68
+ │ ├── tags.njk # All tags page
69
+ │ ├── tag.njk # Individual tag page
70
+ │ └── feed.njk # RSS feed template
71
+ └── styles/
72
+ └── main.css # Theme styles
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Required Files
78
+
79
+ ### Layouts (Required)
80
+
81
+ | File | Purpose |
82
+ |------|---------|
83
+ | `base.njk` | Base HTML structure, includes head and body |
84
+ | `post.njk` | Template for blog posts |
85
+ | `page.njk` | Template for static pages |
86
+ | `note.njk` | Template for notes |
87
+
88
+ ### Pages (Required)
89
+
90
+ | File | Purpose |
91
+ |------|---------|
92
+ | `index.njk` | Homepage |
93
+ | `blog.njk` | Blog listing with pagination |
94
+ | `notes.njk` | Notes listing with pagination |
95
+ | `tags.njk` | All tags overview page |
96
+ | `tag.njk` | Individual tag page with pagination |
97
+ | `feed.njk` | RSS feed (XML) |
98
+
99
+ ### Includes (Recommended)
100
+
101
+ | File | Purpose |
102
+ |------|---------|
103
+ | `header.njk` | Site header and navigation |
104
+ | `footer.njk` | Site footer |
105
+ | `hero.njk` | Homepage hero section |
106
+ | `pagination.njk` | Pagination navigation |
107
+ | `tag-list.njk` | Tag cloud or list |
108
+
109
+ ### Styles (Required)
110
+
111
+ | File | Purpose |
112
+ |------|---------|
113
+ | `main.css` | All theme styles |
114
+
115
+ ---
116
+
117
+ ## Layouts
118
+
119
+ ### base.njk
120
+
121
+ The base layout provides the HTML structure for all pages:
122
+
123
+ ```nunjucks
124
+ <!DOCTYPE html>
125
+ <html lang="en">
126
+ <head>
127
+ {% include "meta.njk" %}
128
+ {% include "theme-script.njk" %}
129
+
130
+ <link rel="stylesheet" href="{{ '/styles/main.css' | url }}">
131
+
132
+ {% block head %}{% endblock %}
133
+ </head>
134
+ <body>
135
+ {% include "header.njk" %}
136
+
137
+ <main class="main">
138
+ {% block content %}{% endblock %}
139
+ </main>
140
+
141
+ {% include "footer.njk" %}
142
+ </body>
143
+ </html>
144
+ ```
145
+
146
+ **Key points:**
147
+ - Include `meta.njk` for SEO meta tags
148
+ - Include `theme-script.njk` to prevent dark mode flash
149
+ - Use `{% block content %}` for page-specific content
150
+ - Use the `url` filter for all paths
151
+
152
+ ### post.njk
153
+
154
+ Template for blog posts:
155
+
156
+ ```nunjucks
157
+ {% extends "base.njk" %}
158
+
159
+ {% block content %}
160
+ <article class="post">
161
+ <header class="post-header">
162
+ <h1 class="post-title">
163
+ {{ page.title }}
164
+ {% if page.draft %}<span class="draft-badge">Draft</span>{% endif %}
165
+ </h1>
166
+
167
+ <div class="post-meta">
168
+ <time datetime="{{ page.date | date('iso') }}">
169
+ {{ page.date | date('long') }}
170
+ </time>
171
+
172
+ {% if page.tags and page.tags.length %}
173
+ <span class="post-tags">
174
+ {% for tag in page.tags %}
175
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag">{{ tag }}</a>
176
+ {% endfor %}
177
+ </span>
178
+ {% endif %}
179
+
180
+ <span class="reading-time">{{ page.content | readingTime }}</span>
181
+ </div>
182
+ </header>
183
+
184
+ <div class="post-content prose">
185
+ {{ content | safe }}
186
+ </div>
187
+
188
+ <footer class="post-footer">
189
+ <a href="{{ '/blog/' | url }}">← Back to Blog</a>
190
+ </footer>
191
+ </article>
192
+ {% endblock %}
193
+ ```
194
+
195
+ ### page.njk
196
+
197
+ Template for static pages:
198
+
199
+ ```nunjucks
200
+ {% extends "base.njk" %}
201
+
202
+ {% block content %}
203
+ <article class="page">
204
+ <header class="page-header">
205
+ <h1 class="page-title">{{ page.title }}</h1>
206
+ </header>
207
+
208
+ <div class="page-content prose">
209
+ {{ content | safe }}
210
+ </div>
211
+ </article>
212
+ {% endblock %}
213
+ ```
214
+
215
+ ### note.njk
216
+
217
+ Template for notes:
218
+
219
+ ```nunjucks
220
+ {% extends "base.njk" %}
221
+
222
+ {% block content %}
223
+ <article class="note">
224
+ <div class="note-content prose">
225
+ {{ content | safe }}
226
+ </div>
227
+
228
+ <footer class="note-footer">
229
+ <time datetime="{{ page.date | date('iso') }}">
230
+ {{ page.date | date('full_time') }}
231
+ </time>
232
+
233
+ {% if page.tags and page.tags.length %}
234
+ <span class="note-tags">
235
+ {% for tag in page.tags %}
236
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/" class="tag">{{ tag }}</a>
237
+ {% endfor %}
238
+ </span>
239
+ {% endif %}
240
+ </footer>
241
+ </article>
242
+
243
+ <nav class="note-nav">
244
+ <a href="{{ '/notes/' | url }}">← All Notes</a>
245
+ </nav>
246
+ {% endblock %}
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Pages
252
+
253
+ ### index.njk (Homepage)
254
+
255
+ ```nunjucks
256
+ {% extends "base.njk" %}
257
+
258
+ {% block content %}
259
+ {% include "hero.njk" %}
260
+
261
+ <section class="recent-posts">
262
+ <h2>Latest Posts</h2>
263
+
264
+ {% for post in collections.posts | limit(5) %}
265
+ <article class="post-card">
266
+ <h3><a href="{{ post.url }}">{{ post.title }}</a></h3>
267
+ <time>{{ post.date | date('short') }}</time>
268
+ <p>{{ post.excerpt }}</p>
269
+ </article>
270
+ {% else %}
271
+ <p>No posts yet.</p>
272
+ {% endfor %}
273
+
274
+ <a href="{{ '/blog/' | url }}">View all posts →</a>
275
+ </section>
276
+
277
+ {% if collections.notes and collections.notes.length %}
278
+ <section class="recent-notes">
279
+ <h2>Recent Notes</h2>
280
+
281
+ {% for note in collections.notes | limit(3) %}
282
+ <article class="note-card">
283
+ <div>{{ note.excerptHtml | safe }}</div>
284
+ <time>{{ note.date | date('full_time') }}</time>
285
+ </article>
286
+ {% endfor %}
287
+ </section>
288
+ {% endif %}
289
+ {% endblock %}
290
+ ```
291
+
292
+ ### blog.njk (Blog Listing)
293
+
294
+ ```nunjucks
295
+ {% extends "base.njk" %}
296
+
297
+ {% block content %}
298
+ <header class="page-header">
299
+ <h1>Blog</h1>
300
+ <p>All posts, newest first</p>
301
+ </header>
302
+
303
+ <div class="post-list">
304
+ {% for post in posts %}
305
+ <article class="post-card">
306
+ <h2>
307
+ <a href="{{ post.url }}">{{ post.title }}</a>
308
+ {% if post.draft %}<span class="draft-badge">Draft</span>{% endif %}
309
+ </h2>
310
+ <div class="post-meta">
311
+ <time>{{ post.date | date('long') }}</time>
312
+ <span>{{ post.content | readingTime }}</span>
313
+ </div>
314
+ {% if post.tags and post.tags.length %}
315
+ <div class="tags">
316
+ {% for tag in post.tags %}
317
+ <a href="{{ '/tags/' | url }}{{ tag | slug }}/">{{ tag }}</a>
318
+ {% endfor %}
319
+ </div>
320
+ {% endif %}
321
+ <p>{{ post.excerpt }}</p>
322
+ </article>
323
+ {% else %}
324
+ <p>No posts yet.</p>
325
+ {% endfor %}
326
+ </div>
327
+
328
+ {% include "pagination.njk" %}
329
+ {% endblock %}
330
+ ```
331
+
332
+ ### tag.njk (Individual Tag Page)
333
+
334
+ ```nunjucks
335
+ {% extends "base.njk" %}
336
+
337
+ {% block content %}
338
+ <header class="page-header">
339
+ <h1>Tagged: {{ tag.name }}</h1>
340
+ <p>{{ tag.count }} item{% if tag.count != 1 %}s{% endif %}</p>
341
+ </header>
342
+
343
+ <div class="post-list">
344
+ {% for post in posts %}
345
+ <article class="post-card">
346
+ <h2><a href="{{ post.url }}">{{ post.title or post.excerpt }}</a></h2>
347
+ <time>{{ post.date | date('long') }}</time>
348
+ </article>
349
+ {% endfor %}
350
+ </div>
351
+
352
+ {% include "pagination.njk" %}
353
+
354
+ <a href="{{ '/tags/' | url }}">← All Tags</a>
355
+ {% endblock %}
356
+ ```
357
+
358
+ ### feed.njk (RSS Feed)
359
+
360
+ ```nunjucks
361
+ <?xml version="1.0" encoding="UTF-8"?>
362
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
363
+ <channel>
364
+ <title>{{ site.title | safe }}</title>
365
+ <description>{{ site.description | safe }}</description>
366
+ <link>{{ site.url | safe }}</link>
367
+ <atom:link href="{{ site.url | safe }}/feed.xml" rel="self" type="application/rss+xml"/>
368
+ <language>en-us</language>
369
+ <lastBuildDate>{{ buildDate | safe }}</lastBuildDate>
370
+ <generator>Sia Static Site Generator</generator>
371
+ {% for post in posts | limit(20) %}
372
+ <item>
373
+ <title><![CDATA[{{ post.title | safe }}]]></title>
374
+ <link>{{ site.url | safe }}{{ post.url | safe }}</link>
375
+ <guid isPermaLink="true">{{ site.url | safe }}{{ post.url | safe }}</guid>
376
+ <pubDate>{{ post.date | date('rss') }}</pubDate>
377
+ {% for tag in post.tags %}
378
+ <category>{{ tag | safe }}</category>
379
+ {% endfor %}
380
+ <description><![CDATA[{{ post.excerpt | safe }}]]></description>
381
+ <content:encoded><![CDATA[{{ post.content | safe }}]]></content:encoded>
382
+ </item>
383
+ {% endfor %}
384
+ </channel>
385
+ </rss>
386
+ ```
387
+
388
+ ---
389
+
390
+ ## Includes
391
+
392
+ ### header.njk
393
+
394
+ ```nunjucks
395
+ <header class="site-header">
396
+ <a href="{{ '/' | url }}" class="site-logo">{{ site.title }}</a>
397
+
398
+ <nav class="site-nav">
399
+ <a href="{{ '/' | url }}">Home</a>
400
+ <a href="{{ '/blog/' | url }}">Blog</a>
401
+ <a href="{{ '/notes/' | url }}">Notes</a>
402
+ <a href="{{ '/tags/' | url }}">Tags</a>
403
+
404
+ {% for p in collections.pages | limit(3) %}
405
+ <a href="{{ p.url }}">{{ p.title }}</a>
406
+ {% endfor %}
407
+
408
+ <button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode">
409
+ <!-- Sun/Moon icons -->
410
+ </button>
411
+ </nav>
412
+ </header>
413
+ ```
414
+
415
+ ### footer.njk
416
+
417
+ ```nunjucks
418
+ <footer class="site-footer">
419
+ <p>&copy; {{ 'now' | date('year') }} {{ site.title }}</p>
420
+ <p>Built with <a href="https://github.com/terrymooreii/sia">Sia</a></p>
421
+ </footer>
422
+ ```
423
+
424
+ ### pagination.njk
425
+
426
+ ```nunjucks
427
+ {% if pagination and pagination.totalPages > 1 %}
428
+ <nav class="pagination">
429
+ <span>Page {{ pagination.pageNumber }} of {{ pagination.totalPages }}</span>
430
+
431
+ {% if pagination.previousUrl %}
432
+ <a href="{{ pagination.previousUrl }}">← Newer</a>
433
+ {% else %}
434
+ <span class="disabled">← Newer</span>
435
+ {% endif %}
436
+
437
+ {% if pagination.nextUrl %}
438
+ <a href="{{ pagination.nextUrl }}">Older →</a>
439
+ {% else %}
440
+ <span class="disabled">Older →</span>
441
+ {% endif %}
442
+ </nav>
443
+ {% endif %}
444
+ ```
445
+
446
+ ### tag-list.njk
447
+
448
+ ```nunjucks
449
+ {% if allTags and allTags.length %}
450
+ <div class="tag-list">
451
+ {% for tag in allTags %}
452
+ <a href="{{ '/tags/' | url }}{{ tag.slug }}/" class="tag">
453
+ {{ tag.name }} ({{ tag.count }})
454
+ </a>
455
+ {% endfor %}
456
+ </div>
457
+ {% endif %}
458
+ ```
459
+
460
+ ### hero.njk
461
+
462
+ The hero section is displayed on the homepage when `config.theme.showHero` is enabled:
463
+
464
+ ```nunjucks
465
+ {% if config.theme.showHero %}
466
+ <section class="hero">
467
+ <h1 class="hero-title">{{ site.title }}</h1>
468
+ <p class="hero-description">{{ site.description }}</p>
469
+ </section>
470
+ {% endif %}
471
+ ```
472
+
473
+ Enable the hero section in your `_config.yml`:
474
+
475
+ ```yaml
476
+ theme:
477
+ name: main
478
+ showHero: true
479
+ ```
480
+
481
+ ---
482
+
483
+ ## Styles
484
+
485
+ ### CSS Structure
486
+
487
+ A typical `main.css` structure:
488
+
489
+ ```css
490
+ /* Reset and base styles */
491
+ *, *::before, *::after {
492
+ box-sizing: border-box;
493
+ }
494
+
495
+ /* CSS Variables for theming */
496
+ :root {
497
+ --color-bg: #ffffff;
498
+ --color-text: #1a1a1a;
499
+ --color-primary: #0066cc;
500
+ --color-muted: #666666;
501
+ --font-sans: system-ui, -apple-system, sans-serif;
502
+ --font-mono: ui-monospace, monospace;
503
+ }
504
+
505
+ [data-theme="dark"] {
506
+ --color-bg: #1a1a1a;
507
+ --color-text: #f0f0f0;
508
+ --color-primary: #66b3ff;
509
+ --color-muted: #999999;
510
+ }
511
+
512
+ body {
513
+ font-family: var(--font-sans);
514
+ background: var(--color-bg);
515
+ color: var(--color-text);
516
+ }
517
+
518
+ /* Layout */
519
+ .main {
520
+ max-width: 800px;
521
+ margin: 0 auto;
522
+ padding: 2rem;
523
+ }
524
+
525
+ /* Typography */
526
+ .prose h1, .prose h2, .prose h3 { ... }
527
+ .prose p { ... }
528
+ .prose a { ... }
529
+ .prose code { ... }
530
+ .prose pre { ... }
531
+
532
+ /* Components */
533
+ .post-card { ... }
534
+ .tag { ... }
535
+ .pagination { ... }
536
+
537
+ /* Header and Footer */
538
+ .site-header { ... }
539
+ .site-footer { ... }
540
+
541
+ /* Responsive */
542
+ @media (max-width: 768px) {
543
+ .main { padding: 1rem; }
544
+ }
545
+ ```
546
+
547
+ ### Important CSS Classes
548
+
549
+ Style these classes for full theme support:
550
+
551
+ | Class | Used For |
552
+ |-------|----------|
553
+ | `.prose` | Content container (markdown output) |
554
+ | `.post-card` | Post preview in listings |
555
+ | `.note-card` | Note preview in listings |
556
+ | `.tag` | Tag links |
557
+ | `.draft-badge` | Draft indicator |
558
+ | `.pagination` | Pagination navigation |
559
+ | `.site-header` | Site header |
560
+ | `.site-footer` | Site footer |
561
+
562
+ ---
563
+
564
+ ## Shared Includes
565
+
566
+ Sia provides shared includes available to all themes:
567
+
568
+ ### meta.njk
569
+
570
+ SEO meta tags (Open Graph, Twitter Cards):
571
+
572
+ ```nunjucks
573
+ {% include "meta.njk" %}
574
+ ```
575
+
576
+ This automatically generates:
577
+ - Basic meta tags (charset, viewport, description)
578
+ - Open Graph tags for social sharing
579
+ - Twitter Card tags
580
+ - Article metadata for blog posts
581
+ - RSS feed link
582
+ - Canonical URL
583
+
584
+ ### theme-script.njk
585
+
586
+ Prevents flash of wrong theme on page load:
587
+
588
+ ```nunjucks
589
+ {% include "theme-script.njk" %}
590
+ ```
591
+
592
+ Include this in `<head>` before stylesheets to prevent a flash of light mode when the user prefers dark mode.
593
+
594
+ ---
595
+
596
+ ## Dark Mode Support
597
+
598
+ ### HTML Structure
599
+
600
+ Use the `data-theme` attribute on `<html>`:
601
+
602
+ ```html
603
+ <html data-theme="light">
604
+ <!-- or -->
605
+ <html data-theme="dark">
606
+ ```
607
+
608
+ ### CSS Variables
609
+
610
+ Define colors for both themes:
611
+
612
+ ```css
613
+ :root {
614
+ --color-bg: #ffffff;
615
+ --color-text: #1a1a1a;
616
+ }
617
+
618
+ [data-theme="dark"] {
619
+ --color-bg: #1a1a1a;
620
+ --color-text: #f0f0f0;
621
+ }
622
+ ```
623
+
624
+ ### Theme Toggle Script
625
+
626
+ Add a toggle button in your header:
627
+
628
+ ```javascript
629
+ const toggle = document.getElementById('theme-toggle');
630
+ const html = document.documentElement;
631
+
632
+ function getPreferredTheme() {
633
+ const saved = localStorage.getItem('theme');
634
+ if (saved) return saved;
635
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
636
+ }
637
+
638
+ function setTheme(theme) {
639
+ html.setAttribute('data-theme', theme);
640
+ localStorage.setItem('theme', theme);
641
+ }
642
+
643
+ // Initialize
644
+ setTheme(getPreferredTheme());
645
+
646
+ // Toggle handler
647
+ toggle.addEventListener('click', () => {
648
+ const current = html.getAttribute('data-theme');
649
+ setTheme(current === 'dark' ? 'light' : 'dark');
650
+ });
651
+ ```
652
+
653
+ ---
654
+
655
+ ## External Theme Packages
656
+
657
+ Sia supports distributing themes as npm packages, making it easy to share themes with the community.
658
+
659
+ ### How Theme Resolution Works
660
+
661
+ When Sia loads a theme, it follows this resolution order:
662
+
663
+ 1. **Built-in themes** - First checks the `themes/` folder in the Sia package
664
+ 2. **npm packages** - If not found, looks for `sia-theme-{name}` in your `package.json` dependencies
665
+ 3. **Fallback** - Falls back to the "main" theme if nothing is found
666
+
667
+ ### Using an External Theme
668
+
669
+ To use an external theme in your Sia site:
670
+
671
+ ```bash
672
+ # Install the theme package
673
+ npm install sia-theme-awesome
674
+ ```
675
+
676
+ Then configure it in your `_config.yml`:
677
+
678
+ ```yaml
679
+ theme:
680
+ name: awesome # Sia will look for sia-theme-awesome
681
+ ```
682
+
683
+ That's it! Sia automatically detects and uses the theme from `node_modules`.
684
+
685
+ ### Theme Package Requirements
686
+
687
+ External theme packages must:
688
+
689
+ 1. **Follow naming convention** - Package name must be `sia-theme-{name}`
690
+ 2. **Export theme directory** - Include an `index.js` that exports the theme path
691
+ 3. **Include required files** - Have `layouts/` and `pages/` directories (minimum)
692
+ 4. **Follow theme structure** - Match the same structure as built-in themes
693
+
694
+ ---
695
+
696
+ ## Creating a Theme Package
697
+
698
+ ### Using the Theme Generator
699
+
700
+ The easiest way to create a new theme is with the built-in generator:
701
+
702
+ ```bash
703
+ sia theme my-awesome-theme
704
+ ```
705
+
706
+ This creates a complete theme package with all necessary files:
707
+
708
+ ```
709
+ sia-theme-my-awesome-theme/
710
+ ├── package.json # npm package configuration
711
+ ├── index.js # Exports theme directory path
712
+ ├── README.md # Theme documentation
713
+ ├── layouts/
714
+ │ ├── base.njk # Base HTML template
715
+ │ ├── post.njk # Blog post layout
716
+ │ ├── page.njk # Static page layout
717
+ │ └── note.njk # Note layout
718
+ ├── includes/
719
+ │ ├── header.njk # Site header/navigation
720
+ │ ├── footer.njk # Site footer
721
+ │ ├── hero.njk # Homepage hero section
722
+ │ ├── pagination.njk # Pagination component
723
+ │ └── tag-list.njk # Tag cloud component
724
+ ├── pages/
725
+ │ ├── index.njk # Homepage
726
+ │ ├── blog.njk # Blog listing
727
+ │ ├── notes.njk # Notes listing
728
+ │ ├── tags.njk # All tags page
729
+ │ ├── tag.njk # Single tag page
730
+ │ └── feed.njk # RSS feed
731
+ └── styles/
732
+ └── main.css # Theme styles
733
+ ```
734
+
735
+ ### Generator Options
736
+
737
+ ```bash
738
+ # Interactive mode (prompts for details)
739
+ sia theme my-theme
740
+
741
+ # Quick mode (skip prompts, use defaults)
742
+ sia theme my-theme --quick
743
+ sia theme my-theme -q
744
+ ```
745
+
746
+ ### package.json Structure
747
+
748
+ The generated `package.json` includes:
749
+
750
+ ```json
751
+ {
752
+ "name": "sia-theme-my-theme",
753
+ "version": "1.0.0",
754
+ "description": "My Theme theme for Sia static site generator",
755
+ "main": "index.js",
756
+ "type": "module",
757
+ "keywords": [
758
+ "sia",
759
+ "sia-theme",
760
+ "static-site",
761
+ "theme"
762
+ ],
763
+ "author": "Your Name",
764
+ "license": "MIT",
765
+ "peerDependencies": {
766
+ "@terrymooreii/sia": ">=2.0.0"
767
+ }
768
+ }
769
+ ```
770
+
771
+ ### index.js Structure
772
+
773
+ The `index.js` exports the theme directory for Sia to locate:
774
+
775
+ ```javascript
776
+ import { fileURLToPath } from 'url';
777
+ import { dirname } from 'path';
778
+
779
+ const __filename = fileURLToPath(import.meta.url);
780
+ const __dirname = dirname(__filename);
781
+
782
+ // Export the theme directory path for Sia to use
783
+ export const themeDir = __dirname;
784
+ export default themeDir;
785
+ ```
786
+
787
+ ### Manual Theme Creation
788
+
789
+ If you prefer to create a theme manually:
790
+
791
+ 1. Create a new directory: `mkdir sia-theme-my-theme`
792
+ 2. Initialize npm: `npm init`
793
+ 3. Set the package name to `sia-theme-{name}`
794
+ 4. Create the required directory structure
795
+ 5. Add an `index.js` that exports the directory path
796
+ 6. Add all required template files
797
+
798
+ ---
799
+
800
+ ## Publishing Your Theme
801
+
802
+ ### Preparing for Publication
803
+
804
+ 1. **Test locally** - Link your theme and test with a Sia site:
805
+
806
+ ```bash
807
+ # In your theme directory
808
+ npm link
809
+
810
+ # In a Sia site directory
811
+ npm link sia-theme-my-theme
812
+ ```
813
+
814
+ 2. **Update package.json** - Add repository, bugs, and homepage URLs:
815
+
816
+ ```json
817
+ {
818
+ "repository": {
819
+ "type": "git",
820
+ "url": "https://github.com/username/sia-theme-my-theme.git"
821
+ },
822
+ "bugs": {
823
+ "url": "https://github.com/username/sia-theme-my-theme/issues"
824
+ },
825
+ "homepage": "https://github.com/username/sia-theme-my-theme#readme"
826
+ }
827
+ ```
828
+
829
+ 3. **Write documentation** - Update the README with:
830
+ - Screenshots of the theme
831
+ - Installation instructions
832
+ - Configuration options
833
+ - Customization tips
834
+
835
+ ### Publishing to npm
836
+
837
+ ```bash
838
+ # Login to npm (if not already)
839
+ npm login
840
+
841
+ # Publish the package
842
+ npm publish
843
+
844
+ # Or publish with public access if scoped
845
+ npm publish --access public
846
+ ```
847
+
848
+ ### Versioning
849
+
850
+ Follow semantic versioning:
851
+
852
+ - **Patch** (1.0.1) - Bug fixes, minor style tweaks
853
+ - **Minor** (1.1.0) - New features, backward-compatible changes
854
+ - **Major** (2.0.0) - Breaking changes to templates or configuration
855
+
856
+ ### Theme Discovery
857
+
858
+ To help users find your theme:
859
+
860
+ 1. Use `sia-theme` in your npm keywords
861
+ 2. Add a clear description
862
+ 3. Include screenshots in your README
863
+ 4. Consider creating a demo site
864
+
865
+ ---
866
+
867
+ ## Customizing Existing Themes
868
+
869
+ You don't need to create a full theme to customize your site.
870
+
871
+ ### Override Layouts
872
+
873
+ Create `_layouts/post.njk` to override the post layout:
874
+
875
+ ```nunjucks
876
+ {% extends "base.njk" %}
877
+
878
+ {% block content %}
879
+ <!-- Your custom post layout -->
880
+ {% endblock %}
881
+ ```
882
+
883
+ ### Override Includes
884
+
885
+ Create `_includes/header.njk` to override the header:
886
+
887
+ ```nunjucks
888
+ <header class="my-custom-header">
889
+ <!-- Your custom header -->
890
+ </header>
891
+ ```
892
+
893
+ ### Custom Styles
894
+
895
+ Create `styles/main.css` to use your own styles instead of the theme's:
896
+
897
+ ```css
898
+ /* Your custom styles */
899
+ ```
900
+
901
+ ### Priority Order
902
+
903
+ Sia loads templates in this order (first found wins):
904
+
905
+ 1. `_layouts/` - Your custom layouts
906
+ 2. `_includes/` - Your custom includes
907
+ 3. Theme layouts (`themes/[theme]/layouts/`)
908
+ 4. Theme includes (`themes/[theme]/includes/`)
909
+ 5. Theme pages (`themes/[theme]/pages/`)
910
+ 6. Shared includes (`themes/_shared/includes/`)
911
+
912
+ ---
913
+
914
+ ## Built-in Themes
915
+
916
+ ### main
917
+
918
+ The default full-featured theme with:
919
+ - Clean, modern design
920
+ - Responsive layout
921
+ - Dark mode support
922
+ - Full tag cloud
923
+ - Reading time estimates
924
+
925
+ ### minimal
926
+
927
+ A simple, content-focused theme with:
928
+ - Minimal styling
929
+ - Fast loading
930
+ - Clean typography
931
+ - Dark mode support
932
+
933
+ ### developer
934
+
935
+ A theme with sidebar navigation:
936
+ - Sidebar with recent posts and tags
937
+ - Code-focused styling
938
+ - Dark mode optimized for code
939
+ - Technical aesthetic
940
+
941
+ ### magazine
942
+
943
+ A publication-style theme with:
944
+ - Grid-based layout
945
+ - Featured post support
946
+ - Image-forward design
947
+ - Dark mode support
948
+
949
+ ---
950
+
951
+ ## Best Practices
952
+
953
+ ### Accessibility
954
+
955
+ - Use semantic HTML (`<article>`, `<nav>`, `<main>`, etc.)
956
+ - Include ARIA labels for interactive elements
957
+ - Ensure sufficient color contrast
958
+ - Support keyboard navigation
959
+
960
+ ### Performance
961
+
962
+ - Minimize CSS file size
963
+ - Use system fonts when possible
964
+ - Lazy load images where appropriate
965
+ - Test on slow connections
966
+
967
+ ### SEO
968
+
969
+ - Always include `meta.njk` for proper meta tags
970
+ - Use proper heading hierarchy (h1 → h2 → h3)
971
+ - Include alt text for images
972
+ - Use canonical URLs
973
+
974
+ ### Responsive Design
975
+
976
+ - Test on multiple screen sizes
977
+ - Use relative units (rem, em, %)
978
+ - Make navigation mobile-friendly
979
+ - Consider touch targets on mobile
980
+
981
+ ### Template Tips
982
+
983
+ 1. **Always use the `url` filter** for paths to support basePath hosting
984
+ 2. **Use `safe` filter** for HTML content
985
+ 3. **Check for empty collections** before iterating
986
+ 4. **Handle missing data** gracefully with conditionals
987
+ 5. **Keep templates DRY** with includes and macros