Skip to content

Commit cbdb5cc

Browse files
Build examples with website
1 parent 885354d commit cbdb5cc

11 files changed

Lines changed: 268 additions & 190 deletions

File tree

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ Write to file (use nixpkgs `writeText`):
4444
pkgs.writeText "index.html" (inputs.niccup.lib.render [ "p" "Hello" ])
4545
```
4646

47+
Some more involved examples:
48+
49+
- [art](examples/art/) - Generative SVG (Sierpinski triangle)
50+
- [blog](examples/blog/) - Multi-page blog with navigation
51+
- [docs](examples/docs/) - NixOS module documentation generator
52+
- [quine](examples/quine/) - Self-rendering page
53+
54+
4755
## Data Model
4856

4957
**Element**: `[ tag-spec attrs? children... ]`
@@ -66,10 +74,10 @@ pkgs.writeText "index.html" (inputs.niccup.lib.render [ "p" "Hello" ])
6674

6775
## API
6876

69-
- `render : expr -> string` Render to minified HTML string.
70-
- `renderPretty : expr -> string` Render to indented, human-readable HTML.
71-
- `raw : string -> node` Mark content as unescaped HTML.
72-
- `comment : string -> node` Emit HTML comment.
77+
- `render : expr -> string` - Render to minified HTML string.
78+
- `renderPretty : expr -> string` - Render to indented, human-readable HTML.
79+
- `raw : string -> node` - Mark content as unescaped HTML.
80+
- `comment : string -> node` - Emit HTML comment.
7381

7482
Exported as `lib` from the flake.
7583

@@ -89,4 +97,4 @@ just example blog # builds only the `blog` example
8997

9098
## License
9199

92-
MIT 2025 - [@embedding-shapes](https://github.com/embedding-shapes)
100+
MIT 2025 - [@embedding-shapes](https://github.com/embedding-shapes)

examples/art/build.nix

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{ pkgs, h }:
2+
3+
let
4+
sierpinski = depth: x: y: size:
5+
if depth == 0 then
6+
let
7+
x1 = x;
8+
y1 = y;
9+
x2 = x + size;
10+
y2 = y;
11+
x3 = x + size / 2;
12+
y3 = y - size * 0.866;
13+
in
14+
[ "polygon" { points = "${toString x1},${toString y1} ${toString x2},${toString y2} ${toString x3},${toString y3}"; } ]
15+
else
16+
let
17+
half = size / 2;
18+
height = size * 0.866;
19+
in [
20+
(sierpinski (depth - 1) x y half)
21+
(sierpinski (depth - 1) (x + half) y half)
22+
(sierpinski (depth - 1) (x + half / 2) (y - height / 2) half)
23+
];
24+
25+
page = h.renderPretty [
26+
"html" { lang = "en"; }
27+
[ "head"
28+
[ "meta" { charset = "utf-8"; } ]
29+
[ "title" "Sierpinski Triangle" ]
30+
[ "style" (h.raw ''
31+
body { margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #1a1a2e; }
32+
svg { max-width: 90vmin; max-height: 90vmin; }
33+
polygon { fill: #e94560; }
34+
'') ]
35+
]
36+
[ "body"
37+
[ "svg" { viewBox = "0 0 100 90"; xmlns = "http://www.w3.org/2000/svg"; }
38+
(sierpinski 6 0.0 86.0 100.0)
39+
]
40+
]
41+
];
42+
43+
in pkgs.runCommand "art" {} ''
44+
mkdir -p $out
45+
cp ${pkgs.writeText "index.html" page} $out/index.html
46+
''

examples/art/flake.nix

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -15,55 +15,8 @@
1515
let
1616
pkgs = import nixpkgs { inherit system; };
1717
h = niccup.lib;
18-
19-
# Sierpinski triangle: recursively subdivide into 3 smaller triangles
20-
sierpinski = depth: x: y: size:
21-
if depth == 0 then
22-
let
23-
# Equilateral triangle pointing up
24-
x1 = x;
25-
y1 = y;
26-
x2 = x + size;
27-
y2 = y;
28-
x3 = x + size / 2;
29-
y3 = y - size * 0.866; # sqrt(3)/2 ≈ 0.866
30-
in
31-
[ "polygon" { points = "${toString x1},${toString y1} ${toString x2},${toString y2} ${toString x3},${toString y3}"; } ]
32-
else
33-
let
34-
half = size / 2;
35-
height = size * 0.866;
36-
in [
37-
(sierpinski (depth - 1) x y half)
38-
(sierpinski (depth - 1) (x + half) y half)
39-
(sierpinski (depth - 1) (x + half / 2) (y - height / 2) half)
40-
];
41-
42-
page = h.renderPretty [
43-
"html" { lang = "en"; }
44-
[ "head"
45-
[ "meta" { charset = "utf-8"; } ]
46-
[ "title" "Sierpinski Triangle" ]
47-
[ "style" (h.raw ''
48-
body { margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #1a1a2e; }
49-
svg { max-width: 90vmin; max-height: 90vmin; }
50-
polygon { fill: #e94560; }
51-
'') ]
52-
]
53-
[ "body"
54-
[ "svg" { viewBox = "0 0 100 90"; xmlns = "http://www.w3.org/2000/svg"; }
55-
(sierpinski 6 0.0 86.0 100.0)
56-
]
57-
]
58-
];
59-
60-
indexHtml = pkgs.writeText "index.html" page;
61-
6218
in {
63-
default = pkgs.runCommand "art" {} ''
64-
mkdir -p $out
65-
cp ${indexHtml} $out/index.html
66-
'';
19+
default = import ./build.nix { inherit pkgs h; };
6720
});
6821
};
6922
}

examples/blog/build.nix

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{ pkgs, h }:
2+
3+
let
4+
posts = [
5+
{ slug = "hello-world"; title = "Hello World"; body = "Welcome to my blog!"; }
6+
{ slug = "second-post"; title = "Second Post"; body = "Another great article."; }
7+
];
8+
9+
header = [ "header" [ "a" { href = "index.html"; } "My Blog" ] ];
10+
footer = [ "footer" [ "p" "Built with niccup" ] ];
11+
12+
nav = [ "nav"
13+
[ "h2" "Posts" ]
14+
[ "ul" (map (p: [ "li" [ "a" { href = "${p.slug}.html"; } p.title ] ]) posts) ]
15+
];
16+
17+
renderPage = { title, body ? null }: h.renderPretty [
18+
"html" { lang = "en"; }
19+
[ "head"
20+
[ "meta" { charset = "utf-8"; } ]
21+
[ "title" title ]
22+
]
23+
[ "body"
24+
header
25+
(if body != null then [ "main" [ "h1" title ] (h.raw body) ] else [])
26+
nav
27+
footer
28+
]
29+
];
30+
31+
in pkgs.runCommand "blog" {} ''
32+
mkdir -p $out
33+
cp ${pkgs.writeText "index.html" (renderPage { title = "My Blog"; })} $out/index.html
34+
${builtins.concatStringsSep "\n" (map (post:
35+
"cp ${pkgs.writeText "${post.slug}.html" (renderPage { inherit (post) title body; })} $out/${post.slug}.html"
36+
) posts)}
37+
''

examples/blog/flake.nix

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,49 +15,8 @@
1515
let
1616
pkgs = import nixpkgs { inherit system; };
1717
h = niccup.lib;
18-
19-
posts = [
20-
{ slug = "hello-world"; title = "Hello World"; body = "Welcome to my blog!"; }
21-
{ slug = "second-post"; title = "Second Post"; body = "Another great article."; }
22-
];
23-
24-
header = [ "header" [ "a" { href = "index.html"; } "My Blog" ] ];
25-
26-
footer = [ "footer" [ "p" "Built with niccup" ] ];
27-
28-
nav = posts: [ "nav"
29-
[ "h2" "Posts" ]
30-
[ "ul" (map (p: [ "li" [ "a" { href = "${p.slug}.html"; } p.title ] ]) posts) ]
31-
];
32-
33-
renderPage = { title, body ? null }: h.renderPretty [
34-
"html" { lang = "en"; }
35-
[ "head"
36-
[ "meta" { charset = "utf-8"; } ]
37-
[ "title" title ]
38-
]
39-
[ "body"
40-
header
41-
(if body != null then [ "main" [ "h1" title ] (h.raw body) ] else [])
42-
(nav posts)
43-
footer
44-
]
45-
];
46-
47-
indexHtml = pkgs.writeText "index.html" (renderPage { title = "My Blog"; });
48-
49-
postPages = map (post:
50-
pkgs.writeText "${post.slug}.html" (renderPage { inherit (post) title body; })
51-
) posts;
52-
5318
in {
54-
default = pkgs.runCommand "blog" {} ''
55-
mkdir -p $out
56-
cp ${indexHtml} $out/index.html
57-
${builtins.concatStringsSep "\n" (map (post:
58-
"cp ${pkgs.writeText "${post.slug}.html" (renderPage { inherit (post) title body; })} $out/${post.slug}.html"
59-
) posts)}
60-
'';
19+
default = import ./build.nix { inherit pkgs h; };
6120
});
6221
};
6322
}

examples/docs/build.nix

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{ pkgs, h, modulePath ? [ "services" "tailscale" ] }:
2+
3+
let
4+
moduleName = builtins.concatStringsSep "." modulePath;
5+
6+
eval = import (pkgs.path + "/nixos") {
7+
configuration = { };
8+
system = pkgs.stdenv.hostPlatform.system;
9+
};
10+
11+
options = import ./options.nix {
12+
inherit (pkgs) lib;
13+
inherit eval modulePath;
14+
};
15+
16+
components = import ./components.nix { inherit h; };
17+
18+
css = builtins.readFile ./style.css;
19+
20+
page = h.renderPretty [ "html" { lang = "en"; }
21+
[ "head"
22+
[ "meta" { charset = "utf-8"; } ]
23+
[ "meta" { name = "viewport"; content = "width=device-width, initial-scale=1"; } ]
24+
[ "title" "${moduleName} - NixOS Options" ]
25+
[ "style" (h.raw css) ]
26+
]
27+
[ "body"
28+
(components.header moduleName "NixOS module configuration options")
29+
[ "main" (map components.optionCard options) ]
30+
(components.footer "Generated from nixpkgs using niccup")
31+
]
32+
];
33+
34+
in pkgs.runCommand "docs-${builtins.replaceStrings ["."] ["-"] moduleName}" {} ''
35+
mkdir -p $out
36+
cp ${pkgs.writeText "index.html" page} $out/index.html
37+
''

examples/docs/flake.nix

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,8 @@
1515
let
1616
pkgs = import nixpkgs { inherit system; };
1717
h = niccup.lib;
18-
19-
modulePath = [ "services" "tailscale" ];
20-
moduleName = builtins.concatStringsSep "." modulePath;
21-
22-
eval = import (pkgs.path + "/nixos") {
23-
configuration = { };
24-
inherit system;
25-
};
26-
27-
options = import ./options.nix {
28-
inherit (pkgs) lib;
29-
inherit eval modulePath;
30-
};
31-
32-
components = import ./components.nix { inherit h; };
33-
34-
css = builtins.readFile ./style.css;
35-
36-
page = h.renderPretty [ "html" { lang = "en"; }
37-
[ "head"
38-
[ "meta" { charset = "utf-8"; } ]
39-
[ "meta" { name = "viewport"; content = "width=device-width, initial-scale=1"; } ]
40-
[ "title" "${moduleName} - NixOS Options" ]
41-
[ "style" (h.raw css) ]
42-
]
43-
[ "body"
44-
(components.header moduleName "NixOS module configuration options")
45-
[ "main" (map components.optionCard options) ]
46-
(components.footer "Generated from nixpkgs using niccup")
47-
]
48-
];
49-
5018
in {
51-
default = pkgs.runCommand "docs-${builtins.replaceStrings ["."] ["-"] moduleName}" {} ''
52-
mkdir -p $out
53-
cp ${pkgs.writeText "index.html" page} $out/index.html
54-
'';
19+
default = import ./build.nix { inherit pkgs h; };
5520
});
5621
};
5722
}

examples/quine/build.nix

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{ pkgs, h }:
2+
3+
let
4+
flakeSrc = builtins.readFile ./flake.nix;
5+
buildSrc = builtins.readFile ./build.nix;
6+
7+
css = ''
8+
body { max-width: 800px; margin: 2rem auto; padding: 0 1rem; font-family: system-ui; background: #fafafa; color: #333; }
9+
@media (prefers-color-scheme: dark) { body { background: #1a1a2e; color: #eee; } }
10+
h1 { border-bottom: 2px solid #0066cc; padding-bottom: 0.5rem; }
11+
h2 { color: #666; margin-top: 1.5rem; font-size: 1rem; font-family: ui-monospace, monospace; }
12+
@media (prefers-color-scheme: dark) { h2 { color: #999; } }
13+
pre { background: #1a1a2e; color: #eee; padding: 1rem; border-radius: 8px; overflow-x: auto; font-size: 0.85rem; line-height: 1.4; }
14+
code { font-family: ui-monospace, monospace; }
15+
.meta { color: #666; font-style: italic; margin-top: 2rem; }
16+
@media (prefers-color-scheme: dark) { .meta { color: #888; } }
17+
'';
18+
19+
page = h.render [
20+
"html" { lang = "en"; }
21+
[ "head"
22+
[ "meta" { charset = "utf-8"; } ]
23+
[ "meta" { name = "viewport"; content = "width=device-width, initial-scale=1"; } ]
24+
[ "title" "Quine - niccup example" ]
25+
[ "style" (h.raw css) ]
26+
]
27+
[ "body"
28+
[ "h1" "This page was generated by:" ]
29+
[ "h2" "flake.nix" ]
30+
[ "pre" [ "code" flakeSrc ] ]
31+
[ "h2" "build.nix" ]
32+
[ "pre" [ "code" buildSrc ] ]
33+
[ "p.meta" "Copy both files, run 'nix build', and you'll get this exact page." ]
34+
]
35+
];
36+
37+
in pkgs.runCommand "quine" {} ''
38+
mkdir -p $out
39+
cp ${pkgs.writeText "index.html" page} $out/index.html
40+
''

examples/quine/flake.lock

Lines changed: 14 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)