vowel 0.2.4 → 0.3.0

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.
Files changed (74) hide show
  1. package/README.md +134 -26
  2. package/bin.js +209 -1
  3. package/config.js +20 -0
  4. package/docs-source/.votive.db +0 -0
  5. package/docs-source/blog/url-ui.md +1 -5
  6. package/docs-source/blog.md +5 -0
  7. package/docs-source/docs/items.md +1 -1
  8. package/docs-source/home.md +1 -1
  9. package/docs-source/output/about.html +1 -0
  10. package/docs-source/output/blog/url-ui.html +8 -0
  11. package/docs-source/output/blog.html +1 -0
  12. package/docs-source/output/default.css +1 -0
  13. package/docs-source/output/docs/deploy.html +34 -0
  14. package/docs-source/output/docs/file-structure.html +24 -0
  15. package/docs-source/output/docs/folder-settings.html +20 -0
  16. package/docs-source/output/docs/images.html +2 -0
  17. package/docs-source/output/docs/items.html +6 -0
  18. package/docs-source/output/docs/pages.html +101 -0
  19. package/docs-source/output/docs/styling.html +18 -0
  20. package/docs-source/output/docs/taxonomies.html +20 -0
  21. package/docs-source/output/docs.html +28 -0
  22. package/docs-source/output/features/cards.html +1 -0
  23. package/docs-source/output/features/editing.html +1 -0
  24. package/docs-source/output/features/emoji.html +1 -0
  25. package/docs-source/output/features/frontmatter.html +1 -0
  26. package/docs-source/output/features/lists.html +1 -0
  27. package/docs-source/output/features/navigation.html +1 -0
  28. package/docs-source/output/features/rich-previews.html +1 -0
  29. package/docs-source/output/features/robots.html +1 -0
  30. package/docs-source/output/features/rss.html +1 -0
  31. package/docs-source/output/features/sitemap.html +1 -0
  32. package/docs-source/output/features/speed.html +1 -0
  33. package/docs-source/output/features/static.html +1 -0
  34. package/docs-source/output/features/taxonomies.html +1 -0
  35. package/docs-source/output/features.html +1 -0
  36. package/docs-source/output/feed.xml +1 -0
  37. package/docs-source/output/index.html +21 -0
  38. package/docs-source/output/reset.css +1 -0
  39. package/docs-source/output/roadmap.html +87 -0
  40. package/docs-source/output/robots.txt +14 -0
  41. package/docs-source/output/sitemap.xml +16 -0
  42. package/docs-source/output/typography.css +1 -0
  43. package/docs-source/settings.md +1 -0
  44. package/getMetadata.js +1 -1
  45. package/index.js +5 -660
  46. package/package.json +18 -3
  47. package/plugins/fonts/index.js +26 -0
  48. package/plugins/icons/index.js +26 -0
  49. package/plugins/images/index.js +45 -0
  50. package/plugins/markdown/index.js +1097 -0
  51. package/plugins/robots/index.js +23 -0
  52. package/plugins/styles/index.js +69 -0
  53. package/plugins/vectors/index.js +38 -0
  54. package/plugins/xml/index.js +196 -0
  55. package/stylesheets/DefaultStyles.css +329 -263
  56. package/stylesheets/ResetStyles.css +119 -123
  57. package/stylesheets/TypographyStyles.css +259 -242
  58. package/docs-source/.vercel/README.txt +0 -11
  59. package/docs-source/.vercel/project.json +0 -1
  60. package/docs-source/assets/styles.css +0 -51
  61. package/docs-source/blog/home.md +0 -5
  62. /package/docs-source/{$features → features}/cards.md +0 -0
  63. /package/docs-source/{$features → features}/editing.md +0 -0
  64. /package/docs-source/{$features → features}/emoji.md +0 -0
  65. /package/docs-source/{$features → features}/frontmatter.md +0 -0
  66. /package/docs-source/{$features → features}/lists.md +0 -0
  67. /package/docs-source/{$features → features}/navigation.md +0 -0
  68. /package/docs-source/{$features → features}/rich-previews.md +0 -0
  69. /package/docs-source/{$features → features}/robots.md +0 -0
  70. /package/docs-source/{$features → features}/rss.md +0 -0
  71. /package/docs-source/{$features → features}/sitemap.md +0 -0
  72. /package/docs-source/{$features → features}/speed.md +0 -0
  73. /package/docs-source/{$features → features}/static.md +0 -0
  74. /package/docs-source/{$features → features}/taxonomies.md +0 -0
package/README.md CHANGED
@@ -5,42 +5,132 @@
5
5
  - Bundled with [Votive](https://github.com/samlfair/votive)
6
6
  - Served with [Voot](https://github.com/samlfair/voot)
7
7
 
8
+ See the docs: [vowel.cc](https://vowel.cc).
9
+
8
10
  ## Roadmap
9
11
 
12
+ ### Launch checklist
13
+
14
+ - [x] CLI
15
+ - [ ] Docs
16
+ - [x] Classes
17
+ - [x] Route patterns
18
+ - [x] Post list metadata
19
+ - [.] Transaction management
20
+
21
+ - [.] Fixes
22
+ - [x] Global nav sublinks
23
+ - [x] Hot reload
24
+ - [.] Explicit asset handling
25
+ - [.] Change "job" to "resources"
26
+ - [.] ReadResource types
27
+ - [.] Processor utilities param
28
+ - [.] Delete old SQLite logic
29
+ - [.] Make metadata explicit in SQLiteTarget type
30
+ - [x] Add SQLite begin/commit logic
31
+ - [.] Remove "loadDatabase"
32
+ - [.] Check all types
33
+ - [.] File handling
34
+ - [x] CSS prefixing
35
+ - [x] Homepage in global nav
36
+ - [x] Metadata in header
37
+ - [x] Page list sort
38
+ - [.] Limit in sort
39
+ - [x] Metafiles
40
+ - [x] Robots.txt
41
+ - [x] Sitemap.xml
42
+ - [x] RSS
43
+ - [x] 404.html (just make 404.md)
44
+ - [ ] Markup
45
+ - [.] Image as figure
46
+ - [ ] Admonitions
47
+ - [x] Heading anchors
48
+ - [ ] GFM emoji
49
+ - [ ] Mermaid
50
+ - [x] Slogan
51
+ - [x] TOC
52
+ - [ ] Non-urgent fixes / improvements
53
+ - [ ] Compare metadata on file update
54
+ - [ ] Prune/cleanup step
55
+ - [ ] Dependency type (file, settings, folder)
56
+ - [ ] New systems
57
+ - [ ] Tags
58
+ - [ ] External links
59
+ - [ ] Syntax highlighting
60
+ - [ ] Social links
61
+
10
62
  ### High priority
11
63
 
12
- - [ ] robots.txt
13
- - [ ] sitemap.xml
64
+ - [x] Global nav
65
+ - [x] Breadcrumbs
66
+ - [x] Page classes
67
+ - [ ] GetMany "path exists" filter
68
+ - [ ] GetMany "extension" filter
69
+ - [ ] Delete settings logic
70
+ - [ ] Update settings logic
71
+ - [ ] Logical operators for filters
72
+ - [ ] Customize menus
73
+ - [ ] More file handling plugins
74
+ - [ ] Fonts
75
+ - [x] .woff
76
+ - [x] .woff2
77
+ - [x] .ttf
78
+ - [x] .otf
79
+ - [ ] Handling
80
+ - [ ] Images
81
+ - [x] .png
82
+ - [x] .jpg
83
+ - [x] .jpeg
84
+ - [x] .webp
85
+ - [x] .gif
86
+ - [ ] Formatting
87
+ - [ ] Icons
88
+ - [x] .ico
89
+ - [ ] Vectors
90
+ - [x] .svg
91
+ - [ ] Styles
92
+ - [x] .css
93
+ - [x] Bundling
94
+ - [ ] PDFs
95
+ - [ ] Videos
96
+ - [ ] Arbitrary data
97
+ - [x] Add homepage to global nav
98
+ - [x] Page lists
99
+ - [x] Dates
100
+ - [x] robots.txt
101
+ - [x] sitemap.xml
14
102
  - [ ] 404.html
15
103
  - [ ] Better URL normalization (see notes)
16
- - [ ] TUI
17
- - [ ] Create settings.md
18
- - [ ] Site title
19
- - [ ] Domain
104
+ - [x] Name casing
105
+ - [x] TUI
106
+ - [x] Create settings.md
107
+ - [x] Site title
108
+ - [x] Domain
20
109
  - [ ] Webmentions
21
- - [ ] Logo
22
- - [ ] Wordmark
110
+ - [x] Logo
111
+ - [x] Wordmark
23
112
  - [ ] Identity (rel=me)
24
- - [ ] Filename breadcrumbs
25
- - [ ] RSS
26
- - [ ] Sitemap
27
- - [ ] Create home.md
113
+ - [x] Filename breadcrumbs
114
+ - [x] RSS
115
+ - [x] Sitemap
116
+ - [x] Create home.md
28
117
  - [ ] Create folder settings files
29
118
  - [ ] Title
30
119
  - [ ] Breadcrumb
31
- - [ ] Custom CSS
120
+ - [ ] Remove index file rules
121
+ - [x] Custom CSS
122
+ - [x] styles.css
123
+ - [ ] Any CSS file
32
124
  - [ ] Tags
33
- - [ ] Customize index fallback
34
125
  - [ ] Date format settings
35
126
  - [ ] ::mark::
36
- - [ ] Infer images
127
+ - [x] Infer images
37
128
  - [ ] Favicon
38
- - [ ] Webmentions
39
- - [ ] HTML boilerplate
40
- - [ ] Page lists
129
+ - [-] Webmentions
130
+ - [x] HTML boilerplate
41
131
  - [ ] View transitions
42
- - [ ] Logo
43
- - [ ] Wordmark
132
+ - [x] Logo
133
+ - [x] Wordmark
44
134
  - [ ] Sort nav items
45
135
  - [ ] Canonical URL
46
136
  - [ ] Handle external links
@@ -52,15 +142,16 @@
52
142
  - [ ] HTML
53
143
  - [ ] RSS
54
144
  - [ ] Sitemap
55
- - [ ] Heading anchors
145
+ - [x] Heading anchors
56
146
  - [ ] Taxonomy pages and smart frontmatter
57
- - [ ] CSS cache busting
58
- - [ ] Slogan in homepage title
147
+ - [x] CSS cache busting
148
+ - [x] Slogan in homepage title
59
149
  - [ ] GFM emojis (:smile:)
60
150
 
61
151
  ### Medium priority
62
152
 
63
153
  - [ ] Tests
154
+ - [ ] Internal backlinks
64
155
  - [ ] Break code into multiple files
65
156
  - [ ] Image optimization (unpic)
66
157
  - [ ] [SVG by mask](https://pqina.nl/blog/set-svg-background-image-fill-color/) and [CSS icons](https://antfu.me/posts/icons-in-pure-css)
@@ -71,7 +162,7 @@
71
162
  - [ ] Deploy
72
163
  - [ ] Cloudflare pages
73
164
  - [ ] GitHub pages
74
- - [ ] Post-publish work (ping webmentions)
165
+ - [-] Post-publish work (ping webmentions)
75
166
  - [ ] [Desktop app](https://blackboard.sh/electrobun/docs/)
76
167
  - [ ] Mermaid
77
168
  - [ ] Codeblock syntax highlighting
@@ -84,8 +175,9 @@
84
175
  - [ ] Date
85
176
  - [ ] TOC
86
177
  - [ ] Versioning/publishing script
87
- - [ ] Verify all element types form Obsidian
178
+ - [ ] Verify all element types from Obsidian
88
179
  - [ ] Order/position/kanban
180
+ - [ ] Next/prev links
89
181
  - [ ] Banner
90
182
  - [ ] CTA
91
183
  - [ ] Menu toggle
@@ -126,7 +218,23 @@ rel=author? rel=bookmark? rel=external?
126
218
 
127
219
  Annotations (see notes)
128
220
 
129
-
130
221
  Interactivity (see notes)
131
222
 
132
223
  Calculated properties?
224
+
225
+
226
+ OG tags to consider:
227
+
228
+ ```
229
+
230
+ /*
231
+ og:type - The type of your object, e.g., "video.movie". Depending on the type you specify, other properties may also be required.
232
+ og:locale - The locale these tags are marked up in. Of the format language_TERRITORY. Default is en_US.
233
+ article:published_time - datetime - When the article was first published.
234
+ article:modified_time - datetime - When the article was last changed.
235
+ article:expiration_time - datetime - When the article is out of date after.
236
+ article:author - profile array - Writers of the article.
237
+ article:section - string - A high-level section name. E.g. Technology
238
+ article:tag - string array - Tag words associated with this article.
239
+ */
240
+ ```
package/bin.js CHANGED
@@ -1,5 +1,213 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import fs from "node:fs/promises"
4
+ import { intro, note, text, select, confirm, spinner, outro, box } from "@clack/prompts"
5
+ import { parse } from "@bomb.sh/args"
6
+ import t from "@bomb.sh/tab"
7
+ import votive from "votive"
8
+ import { styleText } from "node:util"
9
+ import { config } from "./config.js"
10
+
11
+ /** @param {number} ms */
12
+ function wait(ms) {
13
+ return new Promise((resolve) => setTimeout(resolve, ms));
14
+ }
15
+
3
16
  import init from "./index.js"
4
17
 
5
- await init()
18
+ /** @param {boolean} verbose */
19
+ async function removeCache(verbose) {
20
+ try {
21
+ await fs.rm("./.votive.db")
22
+ if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "cache cleared")}`)
23
+ } catch (e) {
24
+ if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "no database cache found")}`)
25
+ }
26
+ }
27
+
28
+ /** @param {boolean} verbose */
29
+ async function removeDB(verbose) {
30
+ try {
31
+ await fs.rm("./output", { recursive: true, force: true })
32
+ if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "output cleared")}`)
33
+ } catch (e) {
34
+ if (verbose) console.info(`${styleText("dim", "loading:")} ${styleText("green", "no output cache found")}`)
35
+ }
36
+ }
37
+
38
+ async function exists(filePath) {
39
+ try {
40
+ await fs.stat(filePath)
41
+ return true
42
+ } catch (e) {
43
+ return false
44
+ }
45
+ }
46
+
47
+ async function wizard() {
48
+
49
+ {
50
+ box(`Welcome!\n\nVowel is a static blog generator. It bundles markdown into HTML.`)
51
+
52
+ await select({
53
+ message: "Proceed",
54
+ options: [
55
+ {
56
+ value: true,
57
+ label: "OK"
58
+ }]
59
+ })
60
+
61
+ box(`Vowel infers most relevant information from common markdown conventions, so it requires very little configuration.
62
+
63
+ The few configurations that Vowel uses live in a 'settings.md' file at the root of your project.`)
64
+
65
+
66
+
67
+
68
+ const proceed = await confirm({
69
+ message: "Generate settings file"
70
+ })
71
+
72
+ if (proceed) {
73
+ const frontmatter = [`---`]
74
+
75
+ box(`Change these configurations by editing 'settings.md'.`)
76
+
77
+ const websiteTitle = await text({
78
+ message: "Website title",
79
+ placeholder: "My Cool Website"
80
+ })
81
+
82
+ frontmatter.push(`title: ${websiteTitle}`)
83
+
84
+ const tagline = await text({
85
+ message: "Website tagline (leave empty to skip)",
86
+ placeholder: "All the news that's fit to blog"
87
+ })
88
+
89
+ if (tagline) {
90
+ frontmatter.push(`tagline: ${tagline}`)
91
+ }
92
+
93
+ box(`Vowel comes loaded with themes. You can override these themes by writing styles in a 'styles.css' file at the root of your project.`)
94
+
95
+ const theme = await select({
96
+ message: "Theme",
97
+ options: [
98
+ {
99
+ value: 'default',
100
+ label: 'Default',
101
+ hint: 'full blog styling'
102
+ },
103
+ {
104
+ value: 'typography',
105
+ label: 'Typography',
106
+ hint: 'sensible typographic presets'
107
+ },
108
+ {
109
+ value: 'reset',
110
+ label: 'Reset',
111
+ hint: 'standard CSS reset'
112
+ },
113
+ {
114
+ value: false,
115
+ label: 'None',
116
+ hint: 'write your own styles in `./styles.css`'
117
+ }
118
+ ]
119
+ })
120
+
121
+ if (theme) {
122
+ frontmatter.push(`theme: ${theme}`)
123
+ }
124
+
125
+ box(`To publish a sitemap and RSS feed, provide the domain that you plan to use.`)
126
+
127
+ const domain = await text({
128
+ message: "Domain",
129
+ placeholder: "thebomb.com"
130
+ })
131
+
132
+ if (domain) {
133
+ frontmatter.push(`domain: ${domain}`)
134
+ }
135
+
136
+ box(`To credit an author on your RSS feed, provide an author.`)
137
+
138
+ const author = await text({
139
+ message: "Author",
140
+ placeholder: "George Constanza"
141
+ })
142
+
143
+ if (author) {
144
+ frontmatter.push(`author: ${author}`)
145
+ }
146
+
147
+ box(`Markdown files are pages. Filenames are breadcrumbs. Folders are website sections. 'home.md' is your homepage.`)
148
+
149
+ const homepage = await confirm({
150
+ message: "Generate homepage"
151
+ })
152
+
153
+ const spin = spinner()
154
+
155
+ spin.start('Setting up')
156
+
157
+ frontmatter.push(`---`)
158
+
159
+ const settings = frontmatter.join(`\n`)
160
+
161
+ const settingsExists = await exists("settings.md")
162
+
163
+ if (!settingsExists) {
164
+ await fs.writeFile("settings.md", settings, "utf-8")
165
+ }
166
+
167
+ const homeExists = await exists("home.md")
168
+
169
+ if (!homeExists) {
170
+ await fs.writeFile("home.md", `# Home\n\nWelcome home!`, "utf-8")
171
+ }
172
+
173
+ spin.clear()
174
+
175
+ await select({
176
+ message: "Ready to go. Vowel will now launch your website.",
177
+ options: [
178
+ {
179
+ value: true,
180
+ label: "Let's go"
181
+ }
182
+ ]
183
+ })
184
+
185
+
186
+ }
187
+
188
+
189
+ }
190
+ }
191
+
192
+
193
+ async function main() {
194
+ const args = parse(process.argv.slice(2))
195
+
196
+ const dbExists = await exists(".votive.db")
197
+
198
+ await removeCache(args.logging === "verbose")
199
+ // await removeDB(args.logging === "verbose")
200
+ await fs.mkdir(config.destinationFolder, { recursive: true })
201
+
202
+ if (!args.skip && !dbExists) {
203
+ const loading = votive({ ...config, verbose: false })
204
+ await wizard()
205
+ await (loading)
206
+ }
207
+
208
+
209
+ init(args)
210
+ }
211
+
212
+
213
+ main()
package/config.js ADDED
@@ -0,0 +1,20 @@
1
+ import vowelImagesPlugin from "./plugins/images/index.js"
2
+ import vowelStylesPlugin from "./plugins/styles/index.js"
3
+ import vowelMarkdownPlugin from "./plugins/markdown/index.js"
4
+ import vowelRobotsPlugin from "./plugins/robots/index.js"
5
+ import vowelXMLPlugin from "./plugins/xml/index.js"
6
+ import vowelVectorPlugin from "./plugins/vectors/index.js"
7
+
8
+ /** @import {VotiveConfig} from "votive" */
9
+ export const config = {
10
+ sourceFolder: ".",
11
+ destinationFolder: "output",
12
+ plugins: [
13
+ vowelMarkdownPlugin,
14
+ vowelImagesPlugin,
15
+ vowelStylesPlugin,
16
+ vowelRobotsPlugin,
17
+ vowelXMLPlugin,
18
+ vowelVectorPlugin
19
+ ]
20
+ }
Binary file
@@ -1,13 +1,9 @@
1
- ---
2
- link: https://www.nngroup.com/articles/url-as-ui/
3
- breadcrumb: URL Design
4
- ---
1
+ # Long Live the URL
5
2
 
6
3
  June 27, 2024
7
4
 
8
5
  ![some things](./dog.jpeg)
9
6
 
10
- # Long Live the URL
11
7
 
12
8
  This article from 1999 about how to write good URLs is awesome. Some tips:
13
9
 
@@ -0,0 +1,5 @@
1
+ # Blog
2
+
3
+ Here's a blog.
4
+
5
+ /blog/**
@@ -7,7 +7,7 @@ To create one, prefix a file or folder with a `$`, such as `$faqs`. The file or
7
7
  ```md
8
8
  Here are answers to common questions:
9
9
 
10
- /$faqs?count=10
10
+ /faqs?count=10
11
11
  ```
12
12
 
13
13
  This also allows richer content, as the item will be displayed with frontmatter.
@@ -35,7 +35,7 @@ Vowel is created by Sam Littlefair. If you have questions or comments, [reach ou
35
35
 
36
36
  # Features
37
37
 
38
- /$features?count=25
38
+ /features/*
39
39
 
40
40
  # Try it
41
41
 
@@ -0,0 +1 @@
1
+ <!doctypehtml><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><meta http-equiv=X-UA-Compatible content=ie-edge><title>About - Vowel</title><meta property=og:title content="About - Vowel"><meta property=og:description content="This is an about page."><meta property=og:url content=/about><link rel=stylesheet href=/default.css?47tfcu05><link rel=stylesheet href=/reset.css?bg1gg3lc><link rel=stylesheet href=/typography.css?z0u4qr1t><meta property=og:site_name><body class=about><header><a class=title href=/ rel=home>Vowel</a><p class=slogan>The easiest way to code a website<nav><a href=/about aria-current=page>About</a><a href=/blog>Blog</a><a href=/docs>Docs</a><a href=/features>Features</a><a href=/roadmap>Roadmap</a></nav></header><main><nav aria-label=Breadcrumbs><a href=/>Home</a><a href=/about aria-current=page>About</a></nav><h1>About</h1><p itemprop=description>This is an about page.<nav aria-label=Contents><ol class="toc-level toc-level-1"></ol></nav></main><aside><nav><ul><li><a href=index.html>Home</a><li><a href=/about.html>About</a><li><a href=/blog.html>Blog</a><ul><li><a href=/blog/url-ui.html>Long Live the URL</a></ul><li><a href=/docs.html>Docs</a><ul><li><a href=/docs/deploy.html>Deploy</a><li><a href=/docs/file-structure.html>Files</a><li><a href=/docs/folder-settings.html>Folders</a><li><a href=/docs/images.html>Images</a><li><a href=/docs/items.html>Items</a><li><a href=/docs/pages.html>Pages</a><li><a href=/docs/styling.html>Styling</a><li><a href=/docs/taxonomies.html>Taxonomies</a></ul><li><a href=/features.html>Features</a><ul><li><a href=/features/cards.html>Cards</a><li><a href=/features/editing.html>Live editing</a><li><a href=/features/emoji.html>Emoji favicons</a><li><a href=/features/frontmatter.html>Frontmatter</a><li><a href=/features/lists.html>Blog</a><li><a href=/features/navigation.html>Navigation</a><li><a href=/features/rich-previews.html>Rich link previews</a><li><a href=/features/robots.html>Robots</a><li><a href=/features/rss.html>RSS</a><li><a href=/features/sitemap.html>Sitemap</a><li><a href=/features/speed.html>Speed</a><li><a href=/features/static.html>Static generation</a><li><a href=/features/taxonomies.html>Taxonomies</a></ul><li><a href=/roadmap.html>Roadmap</a></ul></nav></aside><footer><section class=copyright>© 2026</section><section class=shoutout>Made with <a href=https://vowel.cc>Vowel</a></section></footer>
@@ -0,0 +1,8 @@
1
+ <!doctypehtml><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><meta http-equiv=X-UA-Compatible content=ie-edge><title>Long Live the URL - Vowel</title><meta property=og:title content="Long Live the URL - Vowel"><meta property=og:description content="This article from 1999 about how to write good URLs is awesome. Some tips:"><meta property=og:url content=/blog/url-ui><link rel=stylesheet href=/default.css?c8247kf4><link rel=stylesheet href=/reset.css?8xnhn2cn><link rel=stylesheet href=/typography.css?i5lk5yqt><meta property=og:image content=./dog.jpeg><meta property=og:site_name><body class=blog_url-ui><header><a class=title href=/ rel=home>Vowel</a><p class=slogan>The easiest way to code a website<nav><a href=/about>About</a><a href=/blog>Blog</a><a href=/docs>Docs</a><a href=/features>Features</a><a href=/roadmap>Roadmap</a></nav></header><main><nav aria-label=Breadcrumbs><a href=/>Home</a><a href=/blog/>Blog</a><a href=/blog/url-ui aria-current=page>Long Live the URL</a></nav><h1>Long Live the URL</h1><time datetime=2024-06-27T03:00:00.000Z itemprop=date>June 27, 2024</time><img src=./dog.jpeg itemprop=image><p itemprop=description>This article from 1999 about how to write good URLs is awesome. Some tips:<nav aria-label=Contents><ol class="toc-level toc-level-1"></ol></nav><ul>
2
+ <li>Write in lowercase
3
+ <li>Check for typos
4
+ <li>URLs should be permanent
5
+ <li>Keep URLs short
6
+ </ul>
7
+ <p>But Nielsen's overall view is that URLs are an important part of user experience. They contribute to a website visitor's sense of trust and geography. One of Neilsen's rules dictates that a user should be able to "hack" of the last segment of a URL to get to the parent page.
8
+ <p>Ironically, in 1999 Nielsen thought that URLs and search engines would soon fade into obscurity as better solutions to information management emerged. Lo, 25 years later the URL remains more important than ever. Sometimes the simple solutions is the best one.</main><aside><nav><ul><li><a href=index.html>Home</a><li><a href=/about.html>About</a><li><a href=/blog.html>Blog</a><ul><li><a href=/blog/url-ui.html>Long Live the URL</a></ul><li><a href=/docs.html>Docs</a><ul><li><a href=/docs/deploy.html>Deploy</a><li><a href=/docs/file-structure.html>Files</a><li><a href=/docs/folder-settings.html>Folders</a><li><a href=/docs/images.html>Images</a><li><a href=/docs/items.html>Items</a><li><a href=/docs/pages.html>Pages</a><li><a href=/docs/styling.html>Styling</a><li><a href=/docs/taxonomies.html>Taxonomies</a></ul><li><a href=/features.html>Features</a><ul><li><a href=/features/cards.html>Cards</a><li><a href=/features/editing.html>Live editing</a><li><a href=/features/emoji.html>Emoji favicons</a><li><a href=/features/frontmatter.html>Frontmatter</a><li><a href=/features/lists.html>Blog</a><li><a href=/features/navigation.html>Navigation</a><li><a href=/features/rich-previews.html>Rich link previews</a><li><a href=/features/robots.html>Robots</a><li><a href=/features/rss.html>RSS</a><li><a href=/features/sitemap.html>Sitemap</a><li><a href=/features/speed.html>Speed</a><li><a href=/features/static.html>Static generation</a><li><a href=/features/taxonomies.html>Taxonomies</a></ul><li><a href=/roadmap.html>Roadmap</a></ul></nav></aside><footer><section class=copyright>© 2026</section><section class=shoutout>Made with <a href=https://vowel.cc>Vowel</a></section></footer>
@@ -0,0 +1 @@
1
+ <!doctypehtml><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><meta http-equiv=X-UA-Compatible content=ie-edge><title>Blog - Vowel</title><meta property=og:title content="Blog - Vowel"><meta property=og:description content="Here&#39s a blog."><meta property=og:url content=/blog><link rel=stylesheet href=/default.css?gdz9k908><link rel=stylesheet href=/reset.css?p2z8rcu0><link rel=stylesheet href=/typography.css?7h47d6pa><meta property=og:site_name><body class=blog><header><a class=title href=/ rel=home>Vowel</a><p class=slogan>The easiest way to code a website<nav><a href=/about>About</a><a href=/blog aria-current=page>Blog</a><a href=/docs>Docs</a><a href=/features>Features</a><a href=/roadmap>Roadmap</a></nav></header><main><nav aria-label=Breadcrumbs><a href=/>Home</a><a href=/blog aria-current=page>Blog</a></nav><h1>Blog</h1><p itemprop=description>Here's a blog.<nav aria-label=Contents><ol class="toc-level toc-level-1"></ol></nav><section><article><a href=/blog/url-ui><h1>Long Live the URL</h1><time datetime=2024-06-27T03:00:00.000Z itemprop=date>June 27, 2024</time><img src=./dog.jpeg itemprop=image><p itemprop=description>This article from 1999 about how to write good URLs is awesome. Some tips:</p></a></article></section></main><aside><nav><ul><li><a href=index.html>Home</a><li><a href=/about.html>About</a><li><a href=/blog.html>Blog</a><ul><li><a href=/blog/url-ui.html>Long Live the URL</a></ul><li><a href=/docs.html>Docs</a><ul><li><a href=/docs/deploy.html>Deploy</a><li><a href=/docs/file-structure.html>Files</a><li><a href=/docs/folder-settings.html>Folders</a><li><a href=/docs/images.html>Images</a><li><a href=/docs/items.html>Items</a><li><a href=/docs/pages.html>Pages</a><li><a href=/docs/styling.html>Styling</a><li><a href=/docs/taxonomies.html>Taxonomies</a></ul><li><a href=/features.html>Features</a><ul><li><a href=/features/cards.html>Cards</a><li><a href=/features/editing.html>Live editing</a><li><a href=/features/emoji.html>Emoji favicons</a><li><a href=/features/frontmatter.html>Frontmatter</a><li><a href=/features/lists.html>Blog</a><li><a href=/features/navigation.html>Navigation</a><li><a href=/features/rich-previews.html>Rich link previews</a><li><a href=/features/robots.html>Robots</a><li><a href=/features/rss.html>RSS</a><li><a href=/features/sitemap.html>Sitemap</a><li><a href=/features/speed.html>Speed</a><li><a href=/features/static.html>Static generation</a><li><a href=/features/taxonomies.html>Taxonomies</a></ul><li><a href=/roadmap.html>Roadmap</a></ul></nav></aside><footer><section class=copyright>© 2026</section><section class=shoutout>Made with <a href=https://vowel.cc>Vowel</a></section></footer>
@@ -0,0 +1 @@
1
+ @layer reset,typography;@layer default{:root{--text-color:#212121;--accent-color:#004ab3;--accent-color-hover:#1e6ddc;--border-color:#bababa;--main-background:#fff;--code-background:#f2f2f2;--soft-background:#f2f2f2}@media (prefers-color-scheme:dark){:root{--text-color:#ccc;--accent-color:#1eb0dc;--accent-color-hover:#33c4f0;--border-color:#454545;--main-background:#1f1f1f;--code-background:#0d0d0d;--soft-background:#242424}}body{font-family:var(--font-sans);flex-flow:row-reverse wrap;align-content:start;justify-items:center;gap:4rem 1rem;max-width:min(100ch,100vi - 10vw);margin-inline:auto;display:flex;overflow-y:scroll;& header{flex-basis:100%}& main{flex:60ch}& aside{flex:100%}& footer{min-width:100%}}main,article{flex-flow:wrap;align-items:baseline;column-gap:.5rem;margin-bottom:auto;padding-bottom:4rem;display:flex}:is(main,article)>*{flex-basis:100%}:is(main,article) :is(time,dl:not(.link)){opacity:.8;font-size:.9em}dt{display:none}dd{display:inline}html{color:var(--text-color);background:var(--main-background)}a:where(:not([rel=home])){color:var(--accent-color);&:hover{color:var(--accent-color-hover)}}code,pre{background:var(--code-background)}blockquote{border-left:5px solid var(--border-color)}article{border:1px solid var(--border-color);background:var(--soft-background);border-radius:calc(1rem + 5px);flex-direction:column;padding:2rem;display:flex}th,td{border-bottom:1px solid var(--border-color-softer)}hr{border-top:1px solid var(--text-color)}main>img{margin-bottom:2.5rem}dt{font-size:1.1em}dl.link dt{display:none}dl.link{order:calc(Infinity);margin-top:2.5rem;margin-bottom:0}dl.tags,time{font-size:1.1em}dl.tags dt{display:none}dl.tags :is(dd,ul,li){padding-inline:0;display:inline}dl.tags li{margin-inline:.2em}dl.tags li:before{content:"#"}dl.contents ul{padding:0;list-style:none}dl.contents .depth-2{font-weight:500}dl.contents .depth-3{padding-left:.5em;font-size:.95em}nav[aria-label=Contents]:has(>ol:empty){display:none}nav[aria-label=Contents]{background:var(--soft-background);border:1px solid var(--border-color);border-radius:2px;flex-basis:30%;min-width:max-content;max-width:100%;margin-bottom:2.5em;padding:.8em 1.2em;&:before{content:"Contents";width:max-content;margin-bottom:.5em;font-weight:600;display:block}& ol{width:max-content;margin:0;padding:0;list-style:none;& ol{margin-left:1em}}}header{flex-flow:wrap;align-items:center;column-gap:1em;margin-top:4rem;display:flex;& p.slogan{align-self:flex-end;margin-bottom:.5em;margin-left:auto;font-size:1.5em}& nav{border:1px solid var(--border-color);width:100%;padding:0 6px;&:first-of-type{padding-top:7px}&:last-of-type{padding-bottom:7px}background:var(--soft-background);border-radius:5px;flex-wrap:wrap;justify-content:flex-start;column-gap:2rem;margin-inline:-3px;display:flex}& nav:has(+nav){border-bottom:none;border-bottom-right-radius:0;border-bottom-left-radius:0;&:after{content:"";border-bottom:1px solid var(--border-color);flex-basis:100%;margin-block:.7rem;display:block}}& nav+nav{border-top:none;border-top-left-radius:0;border-top-right-radius:0;margin-top:0}}nav[aria-label=Breadcrumbs]>a:only-child{display:none}nav[aria-label=Breadcrumbs]{text-transform:uppercase;color:var(--border-color);& a{color:unset}}aside nav{margin-inline:auto;&>ul{flex-direction:row;justify-content:center;gap:2em;display:flex}& ul{padding-left:0;list-style:none}& li{margin:0;line-height:1.7em}&>ul>li{&>a{font-weight:500}&>ul{font-size:.9em;line-height:1.2em;& ul{padding-left:1em}}}}footer{text-align:center}p:where([itemprop=description]){color:light-dark(oklch(from currentColor calc(l + .2) c h),oklch(from currentColor calc(l - .2) c h));font-size:1.3em}article a{flex-direction:column;row-gap:1em;display:flex;&>*{margin:0}}article img{order:-1}article a,article a:hover{color:unset}article h1{grid-area:title;font-size:1.6rem}article p{font-size:1.6rem}article dl.icon{grid-area:icon;font-size:1.5em}article dl.icon dd{text-align:right}article .description{grid-area:description}article dt{display:none}article dl.icon{order:-1}aside.alert.note{--icon:"ℹ️";--rgb:71, 139, 230}aside.alert.tip{--icon:"💡";--rgb:87, 171, 90}aside.alert.important{--icon:"💬";--rgb:152, 110, 226}aside.alert.warning{--icon:"⚠️";--rgb:198, 144, 38}aside.alert.caution{--icon:"⛔️";--rgb:229, 83, 75}aside.alert{border-left:3px solid rgb(var(--rgb));background:rgba(var(--rgb), .04);padding:1.5rem 2rem}aside.alert h2{color:rgb(var(--rgb))}aside.alert h2:before{opacity:.9;content:var(--icon);margin-right:.7rem;font-size:.8em}}img{border-radius:5px}
@@ -0,0 +1,34 @@
1
+ <!doctypehtml><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><meta http-equiv=X-UA-Compatible content=ie-edge><title>Deploy - Documentation - Vowel</title><meta property=og:title content="Deploy - Documentation - Vowel"><meta property=og:description content="Get going."><meta property=og:url content=/docs/deploy><link rel=stylesheet href=/default.css?bq97ngho><link rel=stylesheet href=/reset.css?fsu4xldi><link rel=stylesheet href=/typography.css?kj95bs00><meta property=og:site_name><body class=docs_deploy><header><a class=title href=/ rel=home>Vowel</a><p class=slogan>The easiest way to code a website<nav><a href=/about>About</a><a href=/blog>Blog</a><a href=/docs>Docs</a><a href=/features>Features</a><a href=/roadmap>Roadmap</a></nav><nav><a href=/docs/deploy aria-current=page>Deploy</a><a href=/docs/file-structure>Files</a><a href=/docs/folder-settings>Folders</a><a href=/docs/images>Images</a><a href=/docs/items>Items</a><a href=/docs/pages>Pages</a><a href=/docs/styling>Styling</a><a href=/docs/taxonomies>Taxonomies</a></nav></header><main><nav aria-label=Breadcrumbs><a href=/>Home</a><a href=/docs/>Docs</a><a href=/docs/deploy aria-current=page>Deploy</a></nav><h1>Deploy</h1><p itemprop=description>Get going.<nav aria-label=Contents><ol class="toc-level toc-level-1"><li class="toc-item toc-item-h1"><a class="toc-link toc-link-h1"href=#github>GitHub</a><ol class="toc-level toc-level-2"><li class="toc-item toc-item-h2"><a class="toc-link toc-link-h2"href=#npx>npx</a><li class="toc-item toc-item-h2"><a class="toc-link toc-link-h2"href=#npm>npm</a></ol></ol></nav><p><strong>Note on URLs:</strong> This guide includes instructions for Vercel, which specify a deploy option called <code>cleanUrls</code>. This will trim the <code>.html</code> extension from your deployment URLs. If you configure your own deploy, ensure you use clean URLs or your links will not work.
2
+
3
+ <p>If you have a GitHub repository of Markdown files, you can deploy them on Vercel automatically on changes.
4
+ <p>Add a <code>vercel.json</code> file to the root of your project with the following contents:
5
+ <pre><code class=language-js>{
6
+ "cleanUrls": true,
7
+ "outputDirectory": ".output",
8
+ "buildCommand": "npx -p svelte@next -p vowel@latest npx vowel build"
9
+ }
10
+ </code></pre>
11
+ <p>Then create a new Vercel project and connect your GitHub repository.
12
+ <p>When you push changes to your repository, your Vercel project will automatically rebuild.
13
+ <h2 id=npx>npx</h2>
14
+ <p>You can manually build your project with <code>npx</code>. Run this command:
15
+ <pre><code>npx -y -p svelte@next -p vowel@latest npx vowel build
16
+ </code></pre>
17
+ <p>The build command will create a <code>vercel.json</code> file at the root of your project with the necessary configuration for a Vercel deploy. Now deploy to Vercel:
18
+ <pre><code>npx vercel --prod
19
+ </code></pre>
20
+ <p>(The <code>--prod</code> flag will deploy to production.)
21
+ <p>If you don't want to use the Vercel CLI, you can deploy the <code>.output</code> directory to any server that supports static HTML.
22
+ <h2 id=npm>npm</h2>
23
+ <p>If you set up your project with npm, you can include a <code>build</code> command in your <code>package.json</code>:
24
+ <pre><code class=language-diff> "scripts": {
25
+ "dev": "vowel",
26
+ + "build": "vowel build"
27
+ },
28
+ </code></pre>
29
+ <p>Run <code>npm run build</code> (<code>vowel build</code>) to build your website.
30
+ <p>Now you can deploy to Vercel:
31
+ <pre><code>npx vercel --prod
32
+ </code></pre>
33
+ <p>(The <code>--prod</code> flag will deploy to production.)
34
+ <p>If you don't want to use the Vercel CLI, you can deploy the <code>.output</code> directory to any server that supports static HTML.</main><aside><nav><ul><li><a href=index.html>Home</a><li><a href=/about.html>About</a><li><a href=/blog.html>Blog</a><ul><li><a href=/blog/url-ui.html>Long Live the URL</a></ul><li><a href=/docs.html>Docs</a><ul><li><a href=/docs/deploy.html>Deploy</a><li><a href=/docs/file-structure.html>Files</a><li><a href=/docs/folder-settings.html>Folders</a><li><a href=/docs/images.html>Images</a><li><a href=/docs/items.html>Items</a><li><a href=/docs/pages.html>Pages</a><li><a href=/docs/styling.html>Styling</a><li><a href=/docs/taxonomies.html>Taxonomies</a></ul><li><a href=/features.html>Features</a><ul><li><a href=/features/cards.html>Cards</a><li><a href=/features/editing.html>Live editing</a><li><a href=/features/emoji.html>Emoji favicons</a><li><a href=/features/frontmatter.html>Frontmatter</a><li><a href=/features/lists.html>Blog</a><li><a href=/features/navigation.html>Navigation</a><li><a href=/features/rich-previews.html>Rich link previews</a><li><a href=/features/robots.html>Robots</a><li><a href=/features/rss.html>RSS</a><li><a href=/features/sitemap.html>Sitemap</a><li><a href=/features/speed.html>Speed</a><li><a href=/features/static.html>Static generation</a><li><a href=/features/taxonomies.html>Taxonomies</a></ul><li><a href=/roadmap.html>Roadmap</a></ul></nav></aside><footer><section class=copyright>© 2026</section><section class=shoutout>Made with <a href=https://vowel.cc>Vowel</a></section></footer>
@@ -0,0 +1,24 @@
1
+ <!doctypehtml><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><meta http-equiv=X-UA-Compatible content=ie-edge><title>File structure - Documentation - Vowel</title><meta property=og:title content="File structure - Documentation - Vowel"><meta property=og:description content="Organize your files."><meta property=og:url content=/docs/file-structure><link rel=stylesheet href=/default.css?4ke3uiwc><link rel=stylesheet href=/reset.css?wdx8q8xm><link rel=stylesheet href=/typography.css?nfgslefd><meta property=og:site_name><body class=docs_file-structure><header><a class=title href=/ rel=home>Vowel</a><p class=slogan>The easiest way to code a website<nav><a href=/about>About</a><a href=/blog>Blog</a><a href=/docs>Docs</a><a href=/features>Features</a><a href=/roadmap>Roadmap</a></nav><nav><a href=/docs/deploy>Deploy</a><a href=/docs/file-structure aria-current=page>Files</a><a href=/docs/folder-settings>Folders</a><a href=/docs/images>Images</a><a href=/docs/items>Items</a><a href=/docs/pages>Pages</a><a href=/docs/styling>Styling</a><a href=/docs/taxonomies>Taxonomies</a></nav></header><main><nav aria-label=Breadcrumbs><a href=/>Home</a><a href=/docs/>Docs</a><a href=/docs/file-structure aria-current=page>Files</a></nav><h1>File structure</h1><p itemprop=description>Organize your files.<nav aria-label=Contents><ol class="toc-level toc-level-1"></ol></nav><p>Almost every file and folder represents a page on your website:
2
+ <ul>
3
+ <li><code>home.md</code> is your homepage, <code>/</code>
4
+ <li><code>about.md</code> would be your about page, <code>/about</code>
5
+ <li><code>blog/home.md</code> would be your blog homepage, <code>/blog</code>
6
+ <li><code>blog/hello-world.md</code> would be a blog post, <code>/blog/hello-world</code>
7
+ </ul>
8
+ <p>(File names like <code>Hello World.md</code> also seem to work fine so far.))
9
+ <p>Even if a folder doesn't have a <code>home.md</code> file, it will still generate a page, so long as it contains any markdown files. So, if you have a <code>blog</code> folder with posts inside it, Vowel will generate a <code>/blog</code> homepage even if you don't have a <code>/blog/home.md</code> file.
10
+ <p>There are a few special files and folders:
11
+ <ul>
12
+ <li><code>assets/</code> contains some static files
13
+ <ul>
14
+ <li><code>assets/styles.css</code> contains your stylesheet, which will be automatically applied if present
15
+ <li><code>assets/favicon.png</code> is your favicon
16
+ </ul>
17
+
18
+ <li><code>templates/</code> is a commonly-used directory in Obsidian projects, so it will be ignored
19
+ <li>A <code>settings.md</code> file will not generate a page. If you want a page called <code>settings</code>, you can create a <code>/settings/home.md</code> file.
20
+ <li><code>.cache.json</code>
21
+ <li><code>.output/</code>
22
+ <li><code>vercel.json</code>
23
+ </ul>
24
+ <p>Any files prefixed with a period will be ignored, as will any files that are not markdown.</main><aside><nav><ul><li><a href=index.html>Home</a><li><a href=/about.html>About</a><li><a href=/blog.html>Blog</a><ul><li><a href=/blog/url-ui.html>Long Live the URL</a></ul><li><a href=/docs.html>Docs</a><ul><li><a href=/docs/deploy.html>Deploy</a><li><a href=/docs/file-structure.html>Files</a><li><a href=/docs/folder-settings.html>Folders</a><li><a href=/docs/images.html>Images</a><li><a href=/docs/items.html>Items</a><li><a href=/docs/pages.html>Pages</a><li><a href=/docs/styling.html>Styling</a><li><a href=/docs/taxonomies.html>Taxonomies</a></ul><li><a href=/features.html>Features</a><ul><li><a href=/features/cards.html>Cards</a><li><a href=/features/editing.html>Live editing</a><li><a href=/features/emoji.html>Emoji favicons</a><li><a href=/features/frontmatter.html>Frontmatter</a><li><a href=/features/lists.html>Blog</a><li><a href=/features/navigation.html>Navigation</a><li><a href=/features/rich-previews.html>Rich link previews</a><li><a href=/features/robots.html>Robots</a><li><a href=/features/rss.html>RSS</a><li><a href=/features/sitemap.html>Sitemap</a><li><a href=/features/speed.html>Speed</a><li><a href=/features/static.html>Static generation</a><li><a href=/features/taxonomies.html>Taxonomies</a></ul><li><a href=/roadmap.html>Roadmap</a></ul></nav></aside><footer><section class=copyright>© 2026</section><section class=shoutout>Made with <a href=https://vowel.cc>Vowel</a></section></footer>
@@ -0,0 +1,20 @@
1
+ <!doctypehtml><html lang=en><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1.0"><meta http-equiv=X-UA-Compatible content=ie-edge><title>Folder settings - Documentation - Vowel</title><meta property=og:title content="Folder settings - Documentation - Vowel"><meta property=og:description content="Configure your project."><meta property=og:url content=/docs/folder-settings><link rel=stylesheet href=/default.css?y4rw9vtn><link rel=stylesheet href=/reset.css?6ev25jay><link rel=stylesheet href=/typography.css?lm3o88q7><meta property=og:site_name><body class=docs_folder-settings><header><a class=title href=/ rel=home>Vowel</a><p class=slogan>The easiest way to code a website<nav><a href=/about>About</a><a href=/blog>Blog</a><a href=/docs>Docs</a><a href=/features>Features</a><a href=/roadmap>Roadmap</a></nav><nav><a href=/docs/deploy>Deploy</a><a href=/docs/file-structure>Files</a><a href=/docs/folder-settings aria-current=page>Folders</a><a href=/docs/images>Images</a><a href=/docs/items>Items</a><a href=/docs/pages>Pages</a><a href=/docs/styling>Styling</a><a href=/docs/taxonomies>Taxonomies</a></nav></header><main><nav aria-label=Breadcrumbs><a href=/>Home</a><a href=/docs/>Docs</a><a href=/docs/folder-settings aria-current=page>Folders</a></nav><h1>Folder settings</h1><p itemprop=description>Configure your project.<nav aria-label=Contents><ol class="toc-level toc-level-1"></ol></nav><ul>
2
+ <li><code>breadcrumb</code>: A navigation label
3
+ </ul>
4
+ <p>At the root of the project, there should be a global settings file with the <code>breadcrumb</code> property and any of the following properties:
5
+ <ul>
6
+ <li>All folder settings, plus
7
+ <li><code>title</code>: Website title
8
+ <li><code>robots</code>
9
+ <ul>
10
+ <li><code>google</code>: Allow Google Search indexing (<code>true</code>/<code>false</code>)
11
+ <li><code>google_images</code>: Allow Google Image indexing (<code>true</code>/<code>false</code>)
12
+ <li><code>ai</code>: Allow AI training (<code>true</code>/<code>false</code>)
13
+ </ul>
14
+
15
+ <li><code>domain</code>: Full root URL for the website domain (e.g. <code>https://example.com</code>)
16
+ <li><code>author</code>: Website author credit for RSS and copyright
17
+ <li><code>icon</code>: An emoji to use as the site favicon
18
+ <li><code>slogan</code>: The website slogan
19
+ <li><code>logo</code>: The website logo
20
+ </ul></main><aside><nav><ul><li><a href=index.html>Home</a><li><a href=/about.html>About</a><li><a href=/blog.html>Blog</a><ul><li><a href=/blog/url-ui.html>Long Live the URL</a></ul><li><a href=/docs.html>Docs</a><ul><li><a href=/docs/deploy.html>Deploy</a><li><a href=/docs/file-structure.html>Files</a><li><a href=/docs/folder-settings.html>Folders</a><li><a href=/docs/images.html>Images</a><li><a href=/docs/items.html>Items</a><li><a href=/docs/pages.html>Pages</a><li><a href=/docs/styling.html>Styling</a><li><a href=/docs/taxonomies.html>Taxonomies</a></ul><li><a href=/features.html>Features</a><ul><li><a href=/features/cards.html>Cards</a><li><a href=/features/editing.html>Live editing</a><li><a href=/features/emoji.html>Emoji favicons</a><li><a href=/features/frontmatter.html>Frontmatter</a><li><a href=/features/lists.html>Blog</a><li><a href=/features/navigation.html>Navigation</a><li><a href=/features/rich-previews.html>Rich link previews</a><li><a href=/features/robots.html>Robots</a><li><a href=/features/rss.html>RSS</a><li><a href=/features/sitemap.html>Sitemap</a><li><a href=/features/speed.html>Speed</a><li><a href=/features/static.html>Static generation</a><li><a href=/features/taxonomies.html>Taxonomies</a></ul><li><a href=/roadmap.html>Roadmap</a></ul></nav></aside><footer><section class=copyright>© 2026</section><section class=shoutout>Made with <a href=https://vowel.cc>Vowel</a></section></footer>