7 Commits

Author SHA1 Message Date
21dc56aa6d docs: markdown
All checks were successful
Release Standalone Builder / build (release) Successful in 32s
Release Standalone Builder / publish-aur (release) Successful in 35s
Release Standalone Builder / publish-homebrew (release) Successful in 6s
2026-04-01 15:39:54 +02:00
7df5daaa6c branding: table styles 2026-04-01 15:24:37 +02:00
4f74dd5fe0 fix: !! 2026-04-01 15:09:24 +02:00
0751849492 docs: theming 2026-04-01 15:04:57 +02:00
009877ae76 feat: colour palettes
All checks were successful
Lint / shellcheck (push) Successful in 17s
2026-04-01 14:23:12 +02:00
69bd5832e7 Create .gitattributes 2026-04-01 14:22:09 +02:00
b525a5f1c2 branding: new button 2026-04-01 14:21:58 +02:00
42 changed files with 803 additions and 92 deletions

14
.gitattributes vendored Normal file
View File

@@ -0,0 +1,14 @@
* text=auto eol=lf
*.sh text eol=lf
*.awk text eol=lf
*.css text eol=lf
*.html text eol=lf
*.js text eol=lf
*.md text eol=lf
*.conf text eol=lf
*.json text eol=lf
*.xml text eol=lf
*.png binary
*.gif binary
*.svg text eol=lf
*.ico binary

View File

@@ -375,6 +375,13 @@ function rewrite_img_tags(line, out, rest, tag, src, alt, force_inline_tag, e
} else if (is_image_ext(ext_of(src)) && force_inline_tag == "") { } else if (is_image_ext(ext_of(src)) && force_inline_tag == "") {
# Preserve hand-written <img> attributes (style/class/etc) for normal images. # Preserve hand-written <img> attributes (style/class/etc) for normal images.
repl = tag repl = tag
} else if (force_inline_tag != "" && !is_global_url(src) && is_inline_text_ext(ext_of(src))) {
repl = render_code_include(src, 1)
if (repl != "") {
repl = "<pre><code>" repl "</code></pre>"
} else {
repl = render_embed(src, alt, (alt != ""), 1)
}
} else { } else {
repl = render_embed(src, alt, (alt != ""), (force_inline_tag != "")) repl = render_embed(src, alt, (alt != ""), (force_inline_tag != ""))
} }
@@ -399,7 +406,12 @@ function rewrite_double_bang_with_parens(line, out, rest, token, inside, src,
src = substr(inside, sep + 2) src = substr(inside, sep + 2)
sub(/\)$/, "", src) sub(/\)$/, "", src)
repl = render_embed(src, alt, (alt != ""), 1) repl = render_code_include(src, 1)
if (repl != "") {
repl = "<pre><code>" repl "</code></pre>"
} else {
repl = render_embed(src, alt, (alt != ""), 1)
}
out = out pre repl out = out pre repl
rest = post rest = post
} }
@@ -416,7 +428,12 @@ function rewrite_double_bang_bare(line, out, rest, token, src, pre, post, rep
src = token src = token
sub(/^!!\[/, "", src) sub(/^!!\[/, "", src)
sub(/\]$/, "", src) sub(/\]$/, "", src)
repl = render_embed(src, "", 0, 1) repl = render_code_include(src, 1)
if (repl != "") {
repl = "<pre><code>" repl "</code></pre>"
} else {
repl = render_embed(src, "", 0, 1)
}
out = out pre repl out = out pre repl
rest = post rest = post
} }
@@ -576,6 +593,75 @@ function restore_plain_markers(line) {
return line return line
} }
function break_code_double_bang(line, out, rest, pstart, pend, code_content, token, pre, post, inside, sep, src, alt, repl) {
out = ""
rest = line
while (1) {
pstart = index(rest, "<code>")
if (pstart == 0) {
out = out rest
break
}
out = out substr(rest, 1, pstart - 1)
rest = substr(rest, pstart)
pend = index(substr(rest, 7), "</code>")
if (pend == 0) {
out = out rest
break
}
pend = pend + 6
code_content = substr(rest, 7, pend - 7)
rest = substr(rest, pend + 7)
if (match(code_content, /!!\[[^\]]*\]\([^)]*\)/)) {
token = substr(code_content, RSTART, RLENGTH)
pre = substr(code_content, 1, RSTART - 1)
post = substr(code_content, RSTART + RLENGTH)
inside = token
sub(/^!!\[/, "", inside)
sep = index(inside, "](")
alt = substr(inside, 1, sep - 1)
src = substr(inside, sep + 2)
sub(/\)$/, "", src)
repl = render_code_include(src, 1)
if (repl != "") {
repl = "<pre><code>" repl "</code></pre>"
} else {
repl = render_embed(src, alt, (alt != ""), 1)
}
if (repl == "") {
out = out "<code>" code_content "</code>"
} else {
if (pre != "") out = out "<code>" pre "</code>"
out = out repl
if (post != "") out = out "<code>" post "</code>"
}
} else if (match(code_content, /!!\[[^\]]+\]/)) {
token = substr(code_content, RSTART, RLENGTH)
pre = substr(code_content, 1, RSTART - 1)
post = substr(code_content, RSTART + RLENGTH)
src = token
sub(/^!!\[/, "", src)
sub(/\]$/, "", src)
repl = render_code_include(src, 1)
if (repl != "") {
repl = "<pre><code>" repl "</code></pre>"
} else {
repl = render_embed(src, "", 0, 1)
}
if (repl == "") {
out = out "<code>" code_content "</code>"
} else {
if (pre != "") out = out "<code>" pre "</code>"
out = out repl
if (post != "") out = out "<code>" post "</code>"
}
} else {
out = out "<code>" code_content "</code>"
}
}
return out
}
BEGIN { BEGIN {
input_dir = dirname_of(input_file) input_dir = dirname_of(input_file)
in_pre_code = 0 in_pre_code = 0
@@ -601,6 +687,9 @@ BEGIN {
line = apply_td_vertical_align(line) line = apply_td_vertical_align(line)
line = restore_plain_markers(line) line = restore_plain_markers(line)
if (!(in_pre_code || start_pre)) {
line = break_code_double_bang(line)
}
print line print line
if (start_pre && !end_pre) { if (start_pre && !end_pre) {

View File

@@ -46,14 +46,9 @@ function mask(s, t) {
while (match(substr(line, p), /`+/)) { while (match(substr(line, p), /`+/)) {
pstart = p + RSTART - 1 pstart = p + RSTART - 1
plen = RLENGTH plen = RLENGTH
if (plen >= 3) {
out = out substr(line, p, pstart - p + plen)
p = pstart + plen
continue
}
# Found 1 or 2 backticks at pstart # Found backtick sequence at pstart
# Search for closing marker # Search for closing marker of same length
marker = substr(line, pstart, plen) marker = substr(line, pstart, plen)
tail = substr(line, pstart + plen) tail = substr(line, pstart + plen)
mpos = index(tail, marker) mpos = index(tail, marker)
@@ -69,9 +64,20 @@ function mask(s, t) {
# Found match! # Found match!
content = substr(tail, 1, mpos - 1) content = substr(tail, 1, mpos - 1)
out = out substr(line, p, pstart - p) out = out substr(line, p, pstart - p)
if (plen == 2 && substr(content, 1, 1) == " " && substr(content, length(content), 1) == " ") { if (plen >= 2 && substr(content, 1, 1) == " " && substr(content, length(content), 1) == " ") {
content = substr(content, 2, length(content) - 2) content = substr(content, 2, length(content) - 2)
} }
if (content ~ /!!\[/) {
_rb_test = content
gsub(/!!\[[^\]]*\]\([^)]*\)/, "", _rb_test)
gsub(/!!\[[^\]]+\]/, "", _rb_test)
gsub(/[[:space:]]+/, "", _rb_test)
if (_rb_test == "") {
out = out content
p = pstart + plen + mpos + plen - 1
continue
}
}
out = out "<code>" mask(content) "</code>" out = out "<code>" mask(content) "</code>"
p = pstart + plen + mpos + plen - 1 p = pstart + plen + mpos + plen - 1
} else { } else {

View File

@@ -50,7 +50,7 @@ END {
in_pre = 0 in_pre = 0
i = 1 i = 1
while (i <= count) { while (i <= count) {
if (lines[i] ~ /^<pre><code>/) { if (lines[i] ~ /^<pre><code/) {
in_pre = 1 in_pre = 1
print lines[i] print lines[i]
i++ i++

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

49
button.svg Normal file
View File

@@ -0,0 +1,49 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="88"
height="31"
viewBox="0 0 88 31"
>
<defs>
<linearGradient id="bg-grad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#4a3b69" />
<stop offset="100%" stop-color="#352654" />
</linearGradient>
</defs>
<rect width="88" height="31" fill="#000" />
<rect x="1" y="1" width="86" height="29" fill="url(#bg-grad)" />
<text
x="5"
y="12"
font-family="Georgia, 'Times New Roman', Times, serif"
font-size="10"
font-weight="bold"
fill="#debfff"
style="letter-spacing: -0.5px;"
>made</text>
<text
x="5"
y="22"
font-family="Georgia, 'Times New Roman', Times, serif"
font-size="10"
font-weight="bold"
fill="#debfff"
style="letter-spacing: -0.5px;"
>with</text>
<text
x="80"
y="16"
text-anchor="end"
dominant-baseline="central"
font-family="Georgia, 'Times New Roman', Times, serif"
font-size="28"
font-weight="bold"
font-style="italic"
fill="#debfff"
letter-spacing="-2"
>kewt</text>
<rect x="1" y="1" width="86" height="1" fill="#ffffff" fill-opacity="0.2" />
<rect x="1" y="2" width="1" height="27" fill="#ffffff" fill-opacity="0.2" />
<rect x="1" y="29" width="86" height="1" fill="#000000" fill-opacity="0.4" />
<rect x="86" y="2" width="1" height="28" fill="#000000" fill-opacity="0.4" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

30
kewt.sh
View File

@@ -324,6 +324,36 @@ if [ "$watch_mode" = "true" ]; then
if [ -n "$changed" ]; then if [ -n "$changed" ]; then
echo "" echo ""
echo "Change detected, rebuilding..." echo "Change detected, rebuilding..."
if [ "$clean_mode" = "true" ]; then
find "$out" -mindepth 1 -delete 2>/dev/null
fi
load_config "./site.conf"
load_config "$src/site.conf"
asset_version=""
if [ "$versioning" = "true" ]; then
asset_version="?v=$(date +%s)"
fi
template="$src/template.html"
[ -f "$template" ] || template="./template.html"
if [ ! -f "$template" ]; then
template="$KEWT_TMPDIR/default_template.html"
printf '%s\n' "$DEFAULT_TMPL" > "$template"
fi
nav=$(generate_nav "$src")
extra_links=$(nav_links_html)
if [ -n "$extra_links" ]; then
nav="$nav
$extra_links"
fi
if [ -n "$nav_extra" ]; then
nav="$nav
$nav_extra"
fi
build_site build_site
touch "$KEWT_TMPDIR/watch_mark" touch "$KEWT_TMPDIR/watch_mark"
fi fi

View File

@@ -7,6 +7,8 @@ needs_rebuild() {
[ -f "$src/site.conf" ] && [ "$src/site.conf" -nt "$out_file" ] && return 0 [ -f "$src/site.conf" ] && [ "$src/site.conf" -nt "$out_file" ] && return 0
[ -f "$template" ] && [ "$template" -nt "$out_file" ] && return 0 [ -f "$template" ] && [ "$template" -nt "$out_file" ] && return 0
[ -f "$script_dir/styles/$style.css" ] && [ "$script_dir/styles/$style.css" -nt "$out_file" ] && return 0 [ -f "$script_dir/styles/$style.css" ] && [ "$script_dir/styles/$style.css" -nt "$out_file" ] && return 0
[ -f "$script_dir/styles/$style.root.css" ] && [ "$script_dir/styles/$style.root.css" -nt "$out_file" ] && return 0
[ -f "$src/styles.root.css" ] && [ "$src/styles.root.css" -nt "$out_file" ] && return 0
return 1 return 1
} }
@@ -97,7 +99,7 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | while read -r entry; do find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | while read -r entry; do
name="${entry##*/}" name="${entry##*/}"
case "$name" in case "$name" in
template.html|site.conf|style.css|index.md) continue ;; template.html|site.conf|style.css|styles.root.css|index.md) continue ;;
esac esac
if [ -d "$entry" ]; then if [ -d "$entry" ]; then
echo "${name}|- [${name}/](${name}/index.html)" >> "$temp_entries" echo "${name}|- [${name}/](${name}/index.html)" >> "$temp_entries"
@@ -332,8 +334,23 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
fi fi
done done
if [ -f "$script_dir/styles/$style.css" ] && needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then if [ ! -f "$src/styles.css" ] && [ ! -f "$src/style.css" ]; then
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css" if [ -f "$src/styles.root.css" ]; then
_base_css="$script_dir/styles/$style.css"
[ ! -f "$_base_css" ] && _base_css="$script_dir/styles/kewt.css"
if [ ! -f "$out/styles.css" ] || [ "$src/styles.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
merge_root_style "$src/styles.root.css" "$_base_css" "$out/styles.css"
fi
elif [ -f "$script_dir/styles/$style.css" ]; then
if needs_rebuild "$script_dir/styles/$style.css" "$out/styles.css"; then
copy_style_with_resolved_vars "$script_dir/styles/$style.css" "$out/styles.css"
fi
elif [ -f "$script_dir/styles/$style.root.css" ]; then
_base_css="$script_dir/styles/kewt.css"
if [ ! -f "$out/styles.css" ] || [ "$script_dir/styles/$style.root.css" -nt "$out/styles.css" ] || [ "$_base_css" -nt "$out/styles.css" ]; then
merge_root_style "$script_dir/styles/$style.root.css" "$_base_css" "$out/styles.css"
fi
fi
fi fi
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
@@ -343,7 +360,7 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while
out_dir="$out/$dir_rel" out_dir="$out/$dir_rel"
case "${file##*/}" in case "${file##*/}" in
template.html|site.conf|style.css|styles.css) continue ;; template.html|site.conf|style.css|styles.css|styles.root.css) continue ;;
esac esac
if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then if [ "${file##*/}" = "index.md" ] && grep -q '^[[:space:]]*{{LIST}}[[:space:]]*$' "$file" 2>/dev/null; then

View File

@@ -109,6 +109,27 @@ copy_style_with_resolved_vars() {
out_style="$2" out_style="$2"
awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style" awk -f "$awk_dir/replace_variables.awk" "$src_style" > "$out_style"
} }
merge_root_style() {
root_file="$1"
base_css="$2"
out_file="$3"
{
cat "$root_file"
awk '
BEGIN { in_root = 0; brace_depth = 0 }
/^:root[[:space:]]*\{/ { in_root = 1; brace_depth = 1; next }
in_root {
for (i = 1; i <= length($0); i++) {
c = substr($0, i, 1)
if (c == "{") brace_depth++
if (c == "}") { brace_depth--; if (brace_depth == 0) { in_root = 0; next } }
}
next
}
{ print }
' "$base_css"
} | awk -f "$awk_dir/replace_variables.awk" > "$out_file"
}
render_markdown() { render_markdown() {
file="$1" file="$1"
is_home="$2" is_home="$2"

View File

@@ -1,44 +1,54 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener("DOMContentLoaded", function () {
var params = new URLSearchParams(window.location.search); var params = new URLSearchParams(window.location.search);
var query = params.get('q'); var query = params.get("q");
var box = document.getElementById('search-box'); var box = document.getElementById("search-box");
var resultsContainer = document.getElementById('search-results-list'); var resultsContainer = document.getElementById("search-results-list");
if (box && query) box.value = query; if (box && query) box.value = query;
if (!query) { if (!query) {
resultsContainer.innerHTML = '<p>Enter a search term above.</p>'; resultsContainer.innerHTML = "<p>Enter a search term above.</p>";
return; return;
} }
fetch('/search.json') fetch("/search.json")
.then(function(response) { return response.json(); }) .then(function (response) {
.then(function(data) { return response.json();
})
.then(function (data) {
var q = query.toLowerCase(); var q = query.toLowerCase();
var results = data.filter(function(item) { var results = data.filter(function (item) {
return item.title.toLowerCase().indexOf(q) !== -1 || return (
item.content.toLowerCase().indexOf(q) !== -1; item.title.toLowerCase().indexOf(q) !== -1 ||
item.content.toLowerCase().indexOf(q) !== -1
);
}); });
var esc = query.replace(/</g, '&lt;').replace(/>/g, '&gt;'); var esc = query.replace(/</g, "&lt;").replace(/>/g, "&gt;");
if (results.length === 0) { if (results.length === 0) {
resultsContainer.innerHTML = '<p>No results found for "<strong>' + esc + '</strong>".</p>'; resultsContainer.innerHTML =
'<p>No results found for "<strong>' + esc + '</strong>".</p>';
return; return;
} }
var html = '<p>Found ' + results.length + ' result(s) for "<strong>' + esc + '</strong>":</p>'; var html =
results.forEach(function(result) { "<p>Found " +
results.length +
' result(s) for "<strong>' +
esc +
'</strong>":</p>';
results.forEach(function (result) {
var snippet = result.content.substring(0, 200); var snippet = result.content.substring(0, 200);
if (result.content.length > 200) snippet += '...'; if (result.content.length > 200) snippet += "...";
html += '<div class="search-result">'; html += '<div class="search-result">';
html += '<a href="' + result.url + '">' + result.title + '</a>'; html += '<a href="' + result.url + '">' + result.title + "</a>";
if (snippet) html += '<p>' + snippet + '</p>'; if (snippet) html += "<p>" + snippet + "</p>";
html += '</div>'; html += "</div>";
}); });
resultsContainer.innerHTML = html; resultsContainer.innerHTML = html;
}) })
.catch(function() { .catch(function () {
resultsContainer.innerHTML = '<p>Error loading search index.</p>'; resultsContainer.innerHTML = "<p>Error loading search index.</p>";
}); });
}); });

View File

@@ -3,5 +3,7 @@
"description": "A minimalist static site generator inspired by werc", "description": "A minimalist static site generator inspired by werc",
"global": "true", "global": "true",
"install": "make install", "install": "make install",
"scripts": ["kewt"] "scripts": [
"kewt"
]
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -43,7 +43,7 @@ search_in_header = false
include_cw_pages_in_search = false include_cw_pages_in_search = false
``` ```
- `title` - site title - `title` - site title
- `style` - style file name from `./styles` (without `.css`) - `style` - style name from the built-in `styles/` directory. See [Theming](theming.md)
- `lang` - document language, used for the `<html lang="...">` attribute (default: "en") - `lang` - document language, used for the `<html lang="...">` attribute (default: "en")
- `draft_by_default` - default value for the `draft` frontmatter field in new posts created (default: false) - `draft_by_default` - default value for the `draft` frontmatter field in new posts created (default: false)
- `dir_indexes` - generate directory index pages when missing `index.md` - `dir_indexes` - generate directory index pages when missing `index.md`

View File

@@ -13,6 +13,14 @@ title = "Embeds"
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]` If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
## Reality-Breaking Embeds
`\!![link]` and `\!![alt](link)` work even inside inline code blocks. If the content between backticks consists only of `\!![]` embeds, the embed triggers and the content is inlined instead of being rendered as code.
```
`!![/file.sh]`
```
## Typed Embeds ## Typed Embeds
Force specific output regardless of extension: Force specific output regardless of extension:

View File

@@ -39,3 +39,82 @@ This renders as `<dl><dt>Term</dt><dd>Definition</dd></dl>`. Multiple definition
## Emoji Shortcodes ## Emoji Shortcodes
Standard GitHub/MkDocs emoji shortcodes like `:smile:`, `:fire:`, `:rocket:` are automatically replaced with their Unicode emoji equivalents. Shortcodes inside code blocks are left as-is. Standard GitHub/MkDocs emoji shortcodes like `:smile:`, `:fire:`, `:rocket:` are automatically replaced with their Unicode emoji equivalents. Shortcodes inside code blocks are left as-is.
## Pipe Tables
Tables use the GitHub-style syntax:
```md
| Header 1 | Header 2 |
|---|---|
| cell 1 | cell 2 |
| cell 3 | cell 4 |
```
Column alignment is set with colons in the separator:
```md
| Left | Center | Right |
|:---|:---:|---:|
| a | b | c |
```
Tables can drop the header row and with a separator:
```md
|---|---|
| a | b |
```
## Blockquotes
Standard Markdown blockquote syntax using `>`:
```md
> This is a blockquote.
> It can span multiple lines.
```
### Admonitions
Blockquotes that start with a type tag become styled admonition blocks. Five built-in types are supported: `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`.
```md
> [!NOTE]
> This is a note admonition.
```
Custom admonition types can be added via the `custom_admonitions` config option in `site.conf`.
## Task Lists
GFM-style task lists are supported inside **both** ordered and unordered lists:
```md
- [ ] Unchecked item
- [x] Checked item
- Normal item
1. [ ] Unchecked item
2. [x] Checked item
3. Normal item
```
## Reference Links
Markdown reference-style links and images are supported:
```md
[link text][ref]
[ref]: https://example.com "Optional title"
![alt text][img-ref]
[img-ref]: /image.png "Optional title"
```
## Plain Text Blocks
Content inside `<plain>...</plain>` tags is rendered without any Markdown processing
## MFM Font Syntax
Misskey-style font syntax is supported for inline font family changes:
- `$[font.serif text]` - serif font
- `$[font.mono text]` - monospace font
- `$[font.sans text]` - sans-serif font

60
site/docs/theming.md Normal file
View File

@@ -0,0 +1,60 @@
---
title = "Theming"
---
# Theming
*kewt* has a few colour palettes built-in. Set the `style` option in `site.conf` to a theme name to apply it.
## Built-in Themes
| Theme | `style` value | Dark/Light |
|---|---|---|
| Kewt (default) | `kewt` | Light |
| Kewt Light | `kewt-light` | Light |
| Nord | `nord` | Dark |
| Nord Light | `nord-light` | Light |
| Monokai | `mono` | Dark |
| Monokai Light | `mono-light` | Light |
| One Dark | `onedark` | Dark |
| One Light | `onelight` | Light |
| Rose Pine | `rosepine` | Dark |
| Rose Pine Light | `rosepine-light` | Light |
| Solarized | `solarized` | Light |
| Solarized Dark | `solarized-dark` | Dark |
```conf
style = "kewt-light"
```
## How It Works
Each theme is a `.root.css` file containing a `:root` block with CSS custom properties. At build time, *kewt* merges the theme's variables with the base `kewt.css` stylesheet. The base `:root` block is stripped out and replaced with the theme's variables.
## Style Resolution
*kewt* resolves styles in this priority order (highest wins):
1. `site/styles.css` - a full custom stylesheet in your site directory. Overrides everything.
2. `site/styles.root.css` - custom `:root` variables merged with the built-in `kewt.css` base.
3. built-in `<style>.css` - a full stylesheet matching the `style` config value.
4. built-in `<style>.root.css` - `:root` variables merged with `kewt.css`.
If none of these exist, the unmodified `kewt.css` is used
## Custom Themes
To create a custom colour theme, place a `styles.root.css` file in your site directory. The file should contain only a `:root` block with the CSS variables you want to override:
```css
:root {
--bg: #1a1b26;
--fg: #c0caf5;
--fg-link: #7aa2f7;
--fg-heading: #c0caf5;
--code-bg: #24283b;
}
```
Any variables not overridden will fall back to the defaults in `kewt.css`. The `:root` block in the base stylesheet is automatically removed to prevent conflicts.
## Per-Directory Styles
Subdirectories can have their own `styles.css` or `styles.root.css` that apply only to pages in that directory. Per-directory styles follow the same priority.

View File

@@ -20,4 +20,6 @@ kewt --serve [port]
- `--generate-template [path]` writes the default `template.html` to the given path (defaults to `template.html` in the current directory). - `--generate-template [path]` writes the default `template.html` to the given path (defaults to `template.html` in the current directory).
- `--update [dir]` adds any missing keys to `site.conf` and checks `template.html` against the latest default. - `--update [dir]` adds any missing keys to `site.conf` and checks `template.html` against the latest default.
- `--watch` (`-w`) watches for file changes in the source directory and rebuilds automatically. - `--watch` (`-w`) watches for file changes in the source directory and rebuilds automatically.
- `--clean` cleans the output directory before building (default behavior).
- `--no-clean` does not clean the output directory before building. Useful with `--watch` to avoid clearing output on every rebuild.
- `--serve` (`-s`) starts a local HTTP server (python3 or busybox) in the output directory after building. Use with the port number to specify the port. Composable with `--watch`. - `--serve` (`-s`) starts a local HTTP server (python3 or busybox) in the output directory after building. Use with the port number to specify the port. Composable with `--watch`.

View File

@@ -9,4 +9,4 @@ Told you
Probably should've mentioned the catgirl too Probably should've mentioned the catgirl too
| --- | --- | | --- | --- |
| ```![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> | | ```!![/styles.css]``` | <img style="vertical-align: top;" src="catgirl.jpg"> |

View File

@@ -0,0 +1,27 @@
:root {
--bg: #f5f0ff;
--bg-deep: #e8dffa;
--fg: #2d1b4e;
--fg-muted: #7a6898;
--fg-link: #7b3fba;
--fg-heading: #3d2466;
--code-bg: #e8dffa;
--code-border: #d0c0e8;
--code-fg: #2d1b4e;
--code-sel: #b8860b;
--code-prop: #6f42c1;
--code-val: #0366d6;
--code-var: #22863a;
--code-com: #7a6898;
--adm-note-bg: #e0e0f8;
--adm-note-border: #5b6fc4;
--adm-tip-bg: #ddf0dd;
--adm-tip-border: #46a758;
--adm-important-bg: #eeddf5;
--adm-important-border: #8b5fc7;
--adm-warning-bg: #f5f0dd;
--adm-warning-border: #b8860b;
--adm-caution-bg: #f5dddd;
--adm-caution-border: #c44569;
--thead-bg: #e8dffa;
}

View File

@@ -23,6 +23,7 @@
--adm-warning-border: #ffe2bd; --adm-warning-border: #ffe2bd;
--adm-caution-bg: #662d43; --adm-caution-bg: #662d43;
--adm-caution-border: #ffc4d5; --adm-caution-border: #ffc4d5;
--thead-bg: #3d2d5c;
} }
body { body {
@@ -271,6 +272,32 @@ hr {
border-top: 1px solid var(--code-border); border-top: 1px solid var(--code-border);
} }
table {
border-collapse: collapse;
margin: 20px 0;
width: 100%;
}
thead {
border-bottom: 2px solid var(--code-border);
background: var(--thead-bg);
}
th {
font-weight: bold;
text-align: left;
padding: 8px 12px;
}
td {
padding: 8px 12px;
border-top: 1px solid var(--code-border);
}
tr:nth-child(even) {
background: var(--bg-deep);
}
.nav-toggle, .nav-toggle,
.nav-toggle-label { .nav-toggle-label {
display: none; display: none;
@@ -303,7 +330,7 @@ hr {
margin: 0 20px 20px 20px; margin: 0 20px 20px 20px;
} }
.nav-toggle:checked~#side-bar { .nav-toggle:checked ~ #side-bar {
display: block; display: block;
} }

View File

@@ -0,0 +1,27 @@
:root {
--bg: #f5f5f5;
--bg-deep: #e8e8e8;
--fg: #1a1a1a;
--fg-muted: #808080;
--fg-link: #333333;
--fg-heading: #000000;
--code-bg: #e8e8e8;
--code-border: #c0c0c0;
--code-fg: #1a1a1a;
--code-sel: #555555;
--code-prop: #333333;
--code-val: #666666;
--code-var: #444444;
--code-com: #999999;
--adm-note-bg: #e0e0ee;
--adm-note-border: #8888aa;
--adm-tip-bg: #e0eee0;
--adm-tip-border: #88aa88;
--adm-important-bg: #eee0ee;
--adm-important-border: #aa88aa;
--adm-warning-bg: #eeeed0;
--adm-warning-border: #aaaa78;
--adm-caution-bg: #eee0e0;
--adm-caution-border: #aa8888;
--thead-bg: #e8e8e8;
}

27
styles/mono.root.css Normal file
View File

@@ -0,0 +1,27 @@
:root {
--bg: #1a1a1a;
--bg-deep: #111111;
--fg: #d4d4d4;
--fg-muted: #808080;
--fg-link: #e0e0e0;
--fg-heading: #ffffff;
--code-bg: #0d0d0d;
--code-border: #404040;
--code-fg: #d4d4d4;
--code-sel: #b8b8b8;
--code-prop: #e0e0e0;
--code-val: #a0a0a0;
--code-var: #c8c8c8;
--code-com: #585858;
--adm-note-bg: #1a1a2e;
--adm-note-border: #5a5a8a;
--adm-tip-bg: #1a2e1a;
--adm-tip-border: #5a8a5a;
--adm-important-bg: #2e1a2e;
--adm-important-border: #8a5a8a;
--adm-warning-bg: #2e2e1a;
--adm-warning-border: #8a8a5a;
--adm-caution-bg: #2e1a1a;
--adm-caution-border: #8a5a5a;
--thead-bg: #111111;
}

View File

@@ -0,0 +1,27 @@
:root {
--bg: #eceff4;
--bg-deep: #d8dee9;
--fg: #2e3440;
--fg-muted: #4c566a;
--fg-link: #5e81ac;
--fg-heading: #3b4252;
--code-bg: #d8dee9;
--code-border: #c5cdd9;
--code-fg: #2e3440;
--code-sel: #d08770;
--code-prop: #5e81ac;
--code-val: #8fbcbb;
--code-var: #a3be8c;
--code-com: #4c566a;
--adm-note-bg: #d8dee9;
--adm-note-border: #5e81ac;
--adm-tip-bg: #e0ebd8;
--adm-tip-border: #a3be8c;
--adm-important-bg: #e5dbe8;
--adm-important-border: #b48ead;
--adm-warning-bg: #ede5d6;
--adm-warning-border: #ebcb8b;
--adm-caution-bg: #eddcdc;
--adm-caution-border: #bf616a;
--thead-bg: #d8dee9;
}

27
styles/nord.root.css Normal file
View File

@@ -0,0 +1,27 @@
:root {
--bg: #2e3440;
--bg-deep: #242933;
--fg: #d8dee9;
--fg-muted: #a5b0c1;
--fg-link: #88c0d0;
--fg-heading: #eceff4;
--code-bg: #3b4252;
--code-border: #4c566a;
--code-fg: #d8dee9;
--code-sel: #ebcb8b;
--code-prop: #8fbcbb;
--code-val: #81a1c1;
--code-var: #a3be8c;
--code-com: #616e88;
--adm-note-bg: #3b4252;
--adm-note-border: #88c0d0;
--adm-tip-bg: #3b4340;
--adm-tip-border: #a3be8c;
--adm-important-bg: #3b4044;
--adm-important-border: #b48ead;
--adm-warning-bg: #3b4038;
--adm-warning-border: #ebcb8b;
--adm-caution-bg: #3b3840;
--adm-caution-border: #bf616a;
--thead-bg: #3b4252;
}

27
styles/onedark.root.css Normal file
View File

@@ -0,0 +1,27 @@
:root {
--bg: #282c34;
--bg-deep: #21252b;
--fg: #abb2bf;
--fg-muted: #636d83;
--fg-link: #61afef;
--fg-heading: #c8ccd4;
--code-bg: #21252b;
--code-border: #3e4451;
--code-fg: #abb2bf;
--code-sel: #e5c07b;
--code-prop: #e06c75;
--code-val: #56b6c2;
--code-var: #98c379;
--code-com: #5c6370;
--adm-note-bg: #2c313c;
--adm-note-border: #61afef;
--adm-tip-bg: #2c3a30;
--adm-tip-border: #98c379;
--adm-important-bg: #33303c;
--adm-important-border: #c678dd;
--adm-warning-bg: #3a352c;
--adm-warning-border: #e5c07b;
--adm-caution-bg: #3a2c2e;
--adm-caution-border: #e06c75;
--thead-bg: #21252b;
}

27
styles/onelight.root.css Normal file
View File

@@ -0,0 +1,27 @@
:root {
--bg: #fafafa;
--bg-deep: #eaeaeb;
--fg: #383a42;
--fg-muted: #a0a1a7;
--fg-link: #4078f2;
--fg-heading: #383a42;
--code-bg: #eaeaeb;
--code-border: #d4d4d5;
--code-fg: #383a42;
--code-sel: #986801;
--code-prop: #e45649;
--code-val: #0184bc;
--code-var: #50a14f;
--code-com: #a0a1a7;
--adm-note-bg: #e8eefa;
--adm-note-border: #4078f2;
--adm-tip-bg: #e8f5e8;
--adm-tip-border: #50a14f;
--adm-important-bg: #f2e8f5;
--adm-important-border: #a626a4;
--adm-warning-bg: #f5f0e0;
--adm-warning-border: #986801;
--adm-caution-bg: #fae8e8;
--adm-caution-border: #e45649;
--thead-bg: #eaeaeb;
}

View File

@@ -0,0 +1,27 @@
:root {
--bg: #faf4ed;
--bg-deep: #f2e9e1;
--fg: #575279;
--fg-muted: #9893a5;
--fg-link: #907aa9;
--fg-heading: #286983;
--code-bg: #f2e9e1;
--code-border: #dfdad9;
--code-fg: #575279;
--code-sel: #ea9d34;
--code-prop: #b4637a;
--code-val: #56949f;
--code-var: #286983;
--code-com: #9893a5;
--adm-note-bg: #f0e8f5;
--adm-note-border: #907aa9;
--adm-tip-bg: #e8f0ee;
--adm-tip-border: #56949f;
--adm-important-bg: #f0e8f0;
--adm-important-border: #907aa9;
--adm-warning-bg: #f5f0e0;
--adm-warning-border: #ea9d34;
--adm-caution-bg: #f5e5e8;
--adm-caution-border: #b4637a;
--thead-bg: #f2e9e1;
}

27
styles/rosepine.root.css Normal file
View File

@@ -0,0 +1,27 @@
:root {
--bg: #191724;
--bg-deep: #13111e;
--fg: #e0def4;
--fg-muted: #908caa;
--fg-link: #c4a7e7;
--fg-heading: #ebbcba;
--code-bg: #1f1d2e;
--code-border: #26233a;
--code-fg: #e0def4;
--code-sel: #f6c177;
--code-prop: #eb6f92;
--code-val: #9ccfd8;
--code-var: #31748f;
--code-com: #6e6a86;
--adm-note-bg: #1f1d2e;
--adm-note-border: #c4a7e7;
--adm-tip-bg: #1a2332;
--adm-tip-border: #9ccfd8;
--adm-important-bg: #2a1f2e;
--adm-important-border: #c4a7e7;
--adm-warning-bg: #2a251f;
--adm-warning-border: #f6c177;
--adm-caution-bg: #2a1f22;
--adm-caution-border: #eb6f92;
--thead-bg: #1f1d2e;
}

View File

@@ -0,0 +1,27 @@
:root {
--bg: #002b36;
--bg-deep: #001e26;
--fg: #839496;
--fg-muted: #586e75;
--fg-link: #268bd2;
--fg-heading: #93a1a1;
--code-bg: #073642;
--code-border: #094959;
--code-fg: #839496;
--code-sel: #d33682;
--code-prop: #268bd2;
--code-val: #2aa198;
--code-var: #859900;
--code-com: #586e75;
--adm-note-bg: #073642;
--adm-note-border: #268bd2;
--adm-tip-bg: #07382e;
--adm-tip-border: #2aa198;
--adm-important-bg: #2a0736;
--adm-important-border: #d33682;
--adm-warning-bg: #363007;
--adm-warning-border: #b58900;
--adm-caution-bg: #360a07;
--adm-caution-border: #cb4b16;
--thead-bg: #073642;
}

27
styles/solarized.root.css Normal file
View File

@@ -0,0 +1,27 @@
:root {
--bg: #fdf6e3;
--bg-deep: #eee8d5;
--fg: #657b83;
--fg-muted: #93a1a1;
--fg-link: #268bd2;
--fg-heading: #586e75;
--code-bg: #eee8d5;
--code-border: #d3cbb7;
--code-fg: #657b83;
--code-sel: #d33682;
--code-prop: #268bd2;
--code-val: #2aa198;
--code-var: #859900;
--code-com: #93a1a1;
--adm-note-bg: #eee8d5;
--adm-note-border: #268bd2;
--adm-tip-bg: #e8f5e0;
--adm-tip-border: #859900;
--adm-important-bg: #f0e8f5;
--adm-important-border: #6c71c4;
--adm-warning-bg: #fdf0e3;
--adm-warning-border: #b58900;
--adm-caution-bg: #fde8e8;
--adm-caution-border: #dc322f;
--thead-bg: #eee8d5;
}