add initial styling
authorLukas Hägele <lukas.haegele93@web.de>
Sun, 3 Nov 2024 15:20:17 +0000 (16:20 +0100)
committerLukas Hägele <lukas.haegele93@web.de>
Sun, 3 Nov 2024 15:20:17 +0000 (16:20 +0100)
favicon.svg [new file with mode: 0644]
src/html.c
src/main.c
style.css [new file with mode: 0644]

diff --git a/favicon.svg b/favicon.svg
new file mode 100644 (file)
index 0000000..8cf2c98
--- /dev/null
@@ -0,0 +1,2 @@
+<svg width="64px" height="64px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--emojione" preserveAspectRatio="xMidYMid meet"><path d="M32.5 44.3c-13.2 0-24.2-4.1-26.7-9.5C10.4 48.9 7.6 62 32.5 62s22.1-13.1 26.7-27.2c-2.5 5.4-13.5 9.5-26.7 9.5" fill="#3e4347"></path><path d="M20.2 20.5c14.1-3.9 46.5 2 39.4 15.4c3.9 4.4 5.3 9.1-13.3 8.8C17.7 51.4-5.9 37.5 4.5 27c-2.5-5.1 3-7.7 15.7-6.5" fill="#6f7980"></path><path d="M32.5 43.7c12.5 0 22.9-3.9 25.3-9c.2-.5.3-1 .5-1.5c0-.3.1-.5.1-.8c0-6.2-11.6-11.2-25.8-11.2s-25.8 5-25.8 11.2c0 .3 0 .5.1.8c.1.5.3 1 .5 1.5c2.2 5.1 12.6 9 25.1 9" fill="#3e4347"></path><path d="M32.5 43.7c12.5 0 22.9-3.9 25.3-9c0-.2.1-.3.1-.5c-2.1-5.3-12.7-9.3-25.5-9.3S9 28.9 6.9 34.2c.1.2.1.3.1.5c2.6 5.1 13 9 25.5 9" fill="#fbbf67"></path><path d="M36 30.4c-5-.1-11.1 3.9-15.2 3.6c-6.8-.5.2 2.8 5 2.8c3.2 0 5.4-2.9 7.1-4.1c2.4-1.5 8.3-2.2 3.1-2.3" fill="#d33b23"></path><g fill="#ff8736"><path d="M21.2 27.1c-1.3-3.6-8.8 2.3-2.4 3.6c1 .2 3.5-.4 2.4-3.6"></path><path d="M35.7 38.4c-1.6-4.4-10.9 2.9-2.9 4.4c1.2.2 4.3-.5 2.9-4.4"></path><path d="M37.4 25.8c-1.5-3.6-10.8 2.3-2.9 3.6c1.1.1 4.2-.5 2.9-3.6"></path><path d="M52.7 29.7c-.6-3.9-12.5-1.7-4.3 2.4c1.2.6 4.8 1.1 4.3-2.4"></path></g><g fill="#89664c"><path d="M15.1 33.2c-1.1-1.5-2.7-3.5-3.3-2.5c-.9 1.4-1.2 4.2-.5 4.3c5 1 4.6-.6 3.8-1.8"></path><path d="M42.6 30.4c-1.8.8-2.9.2-3.7 1.4c-1.9 2.7 4.6 3.6 5.8 2c.9-1.1-.9-3.9-2.1-3.4"></path><path d="M44.1 37.1c-1.3-.6-1 1.4-3-.2c-1.4-1.1-4.4 1.7-2.4 3.6c2.2 2.1 2.8.1 3.7.3c1.9.3 5.2-2.2 1.7-3.7"></path><path d="M22.1 37.7c-1.2-1.2-3.1 2.2-2.4 3.1c1.5 2 6.9 1.6 8.3.1c1.8-1.8-3-.4-5.9-3.2"></path></g><g fill="#699635"><path d="M17.5 36.7s.1 1.2.3 1.7c-1.3-.5-1.3-2.8-1.3-2.8s-.9.5-.8 1.4c-1.4-1.2-2.6-.6-2.6-.6s1.8 1 2.1 1.7c-1 .1-1.6.4-2 1.2c0 0 2-1 3.5-.3c-.6.5-.3 1.7-.3 1.7c2.4-2.5 3.9-.3 5-.9c-.9-1.2-2.2-1.4-2.2-1.4c-.5-1-1.7-1.7-1.7-1.7"></path><path d="M28.3 26.9c-1.2-.2-2.4.4-2.4.4s1.1.5 1.6.7c-1.1.9-3.1-.3-3.1-.3s0 1 .8 1.4c-1.7.6-1.9 2-1.9 2s1.8-1 2.5-.9c-.5.9-.5 1.6.1 2.3c0 0 .2-2.2 1.6-3.1c.1.8 1.3 1.1 1.3 1.1c-.9-3.3 1.8-3.5 1.8-4.7c-1.4 0-2.3 1.1-2.3 1.1"></path><path d="M51.6 35.9c.5-.5 2.6-.8 2.6-.8s-.9-1-2.7-.5c.4-.8-.2-1.6-.2-1.6s-.9 2.1-2.3 2.1c.4-.5.9-1.5.9-1.5s-1.4.3-2.2 1.2c0 0-1.3-.4-2.5.4c.7 1 3-.5 4.2 2.7c0 0 .7-1 .4-1.7c1.6-.1 3.1 1.6 3.1 1.6c0-.9-.4-1.4-1.3-1.9"></path></g><g opacity=".6" fill="#a6aeb0"><path d="M33.3 11.4C29.6 7.6 32.1 2 32.1 2s-7.8 7.4-2.9 12.5c5.7 5.9 2.9 9.5 2.9 9.5s7-6.6 1.2-12.6"></path><path d="M22.3 14.8c-2.8-2.7-.9-6.8-.9-6.8s-5.8 5.4-2.1 9.1c4.3 4.3 2.1 6.9 2.1 6.9s5.2-4.8.9-9.2"></path><path d="M44.3 14.8c-2.8-2.7-.9-6.8-.9-6.8s-5.8 5.4-2.1 9.1c4.3 4.3 2.1 6.9 2.1 6.9s5.2-4.8.9-9.2"></path></g></svg>
+
index 0fe77f43e663c15293b82624585c053dae1ea72e..e11256c4e9323bfbaa57f287d7ea1b4f2dc9d7b5 100644 (file)
@@ -16,6 +16,7 @@
     X(head)    \
     X(meta)    \
     X(title)   \
+    X(link)    \
     X(style)   \
     X(body)    \
     X(header)  \
@@ -73,7 +74,6 @@ typedef struct html_tag
 
 typedef struct
 {
-    str Title;
     html_tag* Tags;
 } html_meta;
 
@@ -124,7 +124,7 @@ createMeta(arena* Arena)
 }
 
 static inline void
-html_addAttribute(html_element* Element, str Key, str Child, arena* Arena)
+html_prependAttribute(html_element* Element, str Key, str Child, arena* Arena)
 {
     html_attribute* Attribute = ARENA_PUSH_STRUCT(Arena, html_attribute);
     {
@@ -132,6 +132,7 @@ html_addAttribute(html_element* Element, str Key, str Child, arena* Arena)
         Attribute->Child = Child;
     }
 
+    Attribute->Next = Element->Attribute;
     Element->Attribute = Attribute;
 }
 
@@ -163,6 +164,34 @@ html_appendContent(html_element* Target, str ContentStr, arena* Arena)
     html_appendElement(Target->Child, Content);
 }
 
+static inline html_element*
+getTitle(html* Html)
+{
+    html_element* Title = Html->Title;
+    html_element* ChildSentinel = Title->Child;
+    html_element* Content = ChildSentinel->Next;
+
+    return Content;
+}
+
+static void
+html_setTitle(html* Html, str TitleStr)
+{
+    html_element* Title = getTitle(Html);
+
+    ASSERT(Title->ElementName = HTML_ELEMENT_NAME_content);
+    Title->Content = TitleStr;
+}
+
+static str
+html_getTitleStr(html* Html)
+{
+    html_element* Title = getTitle(Html);
+
+    str TitleStr = Title->Content;
+    return TitleStr;
+}
+
 static inline void
 html_appendArticleSection(html* Html, html_element* Section)
 {
@@ -199,7 +228,7 @@ html_appendTagListToArticle(html* Html, str TagsDir, arena* Arena)
                         }
                         str Link = str_endUnbounded(uPath);
 
-                        html_addAttribute(TagLink, STR_LITERAL("href"), Link, Arena);
+                        html_prependAttribute(TagLink, STR_LITERAL("href"), Link, Arena);
                         html_appendContent(TagLink, STR_LITERAL("#"), Arena);
                         html_appendContent(TagLink, Tag->Content, Arena);
                         html_appendChild(TagElement, TagLink);
@@ -223,7 +252,7 @@ html_createDefault(arena* Arena)
 
         html_element* HtmlElement = html_createElement(HTML_ELEMENT_NAME_html, Arena);
         {
-            html_addAttribute(HtmlElement, STR_LITERAL("lang"), STR_LITERAL("de-DE"), Arena);
+            html_prependAttribute(HtmlElement, STR_LITERAL("lang"), STR_LITERAL("de-DE"), Arena);
             html_appendChild(Root, HtmlElement);
 
             html_element* Head = html_createElement(HTML_ELEMENT_NAME_head, Arena);
@@ -232,27 +261,41 @@ html_createDefault(arena* Arena)
 
                 html_element* MetaCharset = html_createElement(HTML_ELEMENT_NAME_meta, Arena);
                 {
-                    html_addAttribute(MetaCharset, STR_LITERAL("charset"), STR_LITERAL("utf-8"), Arena);
+                    html_prependAttribute(MetaCharset, STR_LITERAL("charset"), STR_LITERAL("utf-8"), Arena);
                     html_appendChild(Head, MetaCharset);
                 }
 
                 html_element* MetaViewport = html_createElement(HTML_ELEMENT_NAME_meta, Arena);
                 {
-                    html_addAttribute(MetaViewport, STR_LITERAL("name"), STR_LITERAL("viewport"), Arena);
-                    html_addAttribute(MetaViewport, STR_LITERAL("content"), STR_LITERAL("width=device-width, initial-scale=1"), Arena);
+                    html_prependAttribute(MetaViewport, STR_LITERAL("name"), STR_LITERAL("viewport"), Arena);
+                    html_prependAttribute(MetaViewport, STR_LITERAL("content"), STR_LITERAL("width=device-width, initial-scale=1"), Arena);
                     html_appendChild(Head, MetaViewport);
                 }
 
-                /* todo: add later? */
                 html_element* Title = html_createElement(HTML_ELEMENT_NAME_title, Arena);
                 {
-                    static str Untitled = STR_LITERAL("[untitled]");
                     Html->Title = Title;
 
-                    html_appendContent(Title, Untitled, Arena);
+                    html_appendContent(Title, STR_LITERAL("[untitled]"), Arena);
+                    html_appendContent(Title, STR_LITERAL(" | lhaegele.de"), Arena);
+
                     html_appendChild(Head, Title);
                 }
 
+                html_element* LinkIcon = html_createElement(HTML_ELEMENT_NAME_link, Arena);
+                {
+                    html_prependAttribute(LinkIcon, STR_LITERAL("rel"), STR_LITERAL("icon"), Arena);
+                    html_prependAttribute(LinkIcon, STR_LITERAL("href"), STR_LITERAL("/favicon.svg"), Arena);
+                    html_appendChild(Head, LinkIcon);
+                }
+
+                html_element* LinkCss = html_createElement(HTML_ELEMENT_NAME_link, Arena);
+                {
+                    html_prependAttribute(LinkCss, STR_LITERAL("rel"), STR_LITERAL("stylesheet"), Arena);
+                    html_prependAttribute(LinkCss, STR_LITERAL("href"), STR_LITERAL("/style.css"), Arena);
+                    html_appendChild(Head, LinkCss);
+                }
+
                 /* todo: add style? */
             }
 
@@ -266,8 +309,16 @@ html_createDefault(arena* Arena)
 
                     html_element* Heading = html_createElement(HTML_ELEMENT_NAME_h1, Arena);
                     {
-                        html_appendContent(Heading, STR_LITERAL("Meine Rezeptsammlung"), Arena);
                         html_appendChild(Header, Heading);
+
+                        html_element* HeadingLink = html_createElement(HTML_ELEMENT_NAME_a, Arena);
+                        {
+                            html_prependAttribute(HeadingLink, STR_LITERAL("href"), STR_LITERAL("/"), Arena);
+                            html_appendContent(HeadingLink, STR_LITERAL("Meine Rezeptsammlung"), Arena);
+                            html_appendChild(Heading, HeadingLink);
+                        }
+
+                        html_setTitle(Html, STR_LITERAL("Meine Rezeptsammlung"));
                     }
                 }
 
@@ -347,7 +398,7 @@ parseHeading(str Line, html* Html, arena* Arena)
 
         if (Heading->ElementName == HTML_ELEMENT_NAME_h1)
         {
-            Html->Meta->Title = HeadingContent;
+            html_setTitle(Html, HeadingContent);
         }
     }
 
@@ -603,7 +654,12 @@ serializeElement(str* Target, html_element* Parent)
             }
             str_append(Target, STR_LITERAL(">"));
 
-            if (Child->ElementName != HTML_ELEMENT_NAME_meta)
+            if ((Child->ElementName == HTML_ELEMENT_NAME_meta) ||
+                (Child->ElementName == HTML_ELEMENT_NAME_link))
+            {
+                /* void elements don't have a closing tag */
+            }
+            else
             {
                 serializeElement(Target, Child);
 
@@ -615,26 +671,6 @@ serializeElement(str* Target, html_element* Parent)
     }
 }
 
-static void
-html_setTitle(html* Html, str TitleStr)
-{
-    html_element* Title = Html->Title;
-    html_element* ChildSentinel = Title->Child;
-    html_element* Content = ChildSentinel->Next;
-
-    ASSERT(Content->ElementName = HTML_ELEMENT_NAME_content);
-    Content->Content = TitleStr;
-}
-
-static str
-html_getTitle(html* Html)
-{
-    html_meta* Meta = Html->Meta;
-
-    str Title = Meta->Title;
-    return Title;
-}
-
 static str
 html_toString(html* Html, arena* Arena)
 {
index 1afec6c1fa579b1fa2837608d5c83b299a8bf3e7..d8f660c0c3d0cf2ed7e5d973459d6d113d4618e5 100644 (file)
@@ -6,8 +6,6 @@
  *  static site generator for my recipe collection
  *
  * @todo
- *  - (german) unicode support
- *
  *  - markdown parser
  *    - multithreaded (one thread per recipe)
  *    - output status from main thread?
@@ -17,6 +15,8 @@
  *      - date created
  *      - date changed (optional)
  *
+ *  - use `section` on landing page for recipe and tag sections
+ *
  *  - images (optional)
  *
  *  - output to html directory
@@ -232,7 +232,7 @@ appendRecipeLink(html_element* RecipeElement, recipe* Recipe, arena* Arena)
         }
         str Link = str_endUnbounded(uPath);
 
-        html_addAttribute(RecipeLink, STR_LITERAL("href"), Link, Arena);
+        html_prependAttribute(RecipeLink, STR_LITERAL("href"), Link, Arena);
         html_appendContent(RecipeLink, Recipe->Name, Arena);
         html_appendChild(RecipeElement, RecipeLink);
     }
@@ -435,7 +435,7 @@ main(int ArgumentCount, char** Arguments)
         }
 
         Recipe->Html = html_parseMarkdown(FileStr, &Context.MainArena);
-        Recipe->Name = html_getTitle(Recipe->Html);
+        Recipe->Name = html_getTitleStr(Recipe->Html);
     }
 
     /* allocate tag sentinel */
@@ -505,8 +505,29 @@ main(int ArgumentCount, char** Arguments)
             /* todo: compress site creation? */
             html* MainPage = html_createDefault(&Context.MainArena);
             {
-                /* todo: remove title from overview? */
-                html_setTitle(MainPage, STR_LITERAL("Überblick"));
+                /* recipes */
+                html_element* Recipes = html_createElement(HTML_ELEMENT_NAME_h2, &Context.MainArena);
+                {
+                    html_appendArticleSection(MainPage, Recipes);
+                    html_appendContent(Recipes, STR_LITERAL("Rezepte"), &Context.MainArena);
+
+                    html_element* RecipeList = html_createElement(HTML_ELEMENT_NAME_ul, &Context.MainArena);
+                    {
+                        html_appendArticleSection(MainPage, RecipeList);
+                        html_prependAttribute(RecipeList, STR_LITERAL("class"), STR_LITERAL("recipes"), &Context.MainArena);
+
+                        for (recipe* Recipe = Context.Recipes->Next;
+                             Recipe != Context.Recipes;
+                             Recipe = Recipe->Next)
+                        {
+                            html_element* RecipeElement = html_createElement(HTML_ELEMENT_NAME_li, &Context.MainArena);
+                            {
+                                html_appendChild(RecipeList, RecipeElement);
+                                appendRecipeLink(RecipeElement, Recipe, &Context.MainArena);
+                            }
+                        }
+                    }
+                }
 
                 /* tags */
                 html_element* Tags = html_createElement(HTML_ELEMENT_NAME_h2, &Context.MainArena);
@@ -517,6 +538,7 @@ main(int ArgumentCount, char** Arguments)
                     html_element* TagList = html_createElement(HTML_ELEMENT_NAME_ul, &Context.MainArena);
                     {
                         html_appendArticleSection(MainPage, TagList);
+                        html_prependAttribute(TagList, STR_LITERAL("class"), STR_LITERAL("tags"), &Context.MainArena);
 
                         tag* Sentinel = Context.Tags;
                         for (tag* Tag = Sentinel->Next; Tag != Sentinel; Tag = Tag->Next)
@@ -535,7 +557,7 @@ main(int ArgumentCount, char** Arguments)
                                     }
                                     str Link = str_endUnbounded(uPath);
 
-                                    html_addAttribute(TagLink, STR_LITERAL("href"), Link, &Context.MainArena);
+                                    html_prependAttribute(TagLink, STR_LITERAL("href"), Link, &Context.MainArena);
                                     html_appendContent(TagLink, STR_LITERAL("#"), &Context.MainArena);
                                     html_appendContent(TagLink, Tag->Name, &Context.MainArena);
                                     html_appendChild(TagElement, TagLink);
@@ -545,29 +567,6 @@ main(int ArgumentCount, char** Arguments)
                     }
                 }
 
-                /* recipes */
-                html_element* Recipes = html_createElement(HTML_ELEMENT_NAME_h2, &Context.MainArena);
-                {
-                    html_appendArticleSection(MainPage, Recipes);
-                    html_appendContent(Recipes, STR_LITERAL("Rezepte"), &Context.MainArena);
-
-                    html_element* RecipeList = html_createElement(HTML_ELEMENT_NAME_ul, &Context.MainArena);
-                    {
-                        html_appendArticleSection(MainPage, RecipeList);
-
-                        for (recipe* Recipe = Context.Recipes->Next;
-                             Recipe != Context.Recipes;
-                             Recipe = Recipe->Next)
-                        {
-                            html_element* RecipeElement = html_createElement(HTML_ELEMENT_NAME_li, &Context.MainArena);
-                            {
-                                html_appendChild(RecipeList, RecipeElement);
-                                appendRecipeLink(RecipeElement, Recipe, &Context.MainArena);
-                            }
-                        }
-                    }
-                }
-
                 /* serialize html tree */
                 {
                     arena TempArena = Context.MainArena;
@@ -663,6 +662,7 @@ main(int ArgumentCount, char** Arguments)
     }
 
     fprintf(stdout, "Done.\n");
+    /* todo: output stats (memory consumption, runtime, ...) */
 
     return 0;
 }
diff --git a/style.css b/style.css
new file mode 100644 (file)
index 0000000..3c6518c
--- /dev/null
+++ b/style.css
@@ -0,0 +1,71 @@
+:root {
+    --header-color: white;
+    --text-color: rgb(200 200 200);
+    --visited-color: rgb(150 150 150);
+}
+
+body {
+    display: flex;
+    flex-direction: column;
+    margin: 0px;
+}
+
+header {
+    background-color: rgb(10 10 10);
+    color: var(--header-color);
+    display: flex;
+    justify-content: center;
+}
+
+header > h1 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+}
+
+header a:link, a:visited, a:hover {
+    color: var(--header-color);
+    text-decoration: none;
+}
+
+main {
+    background-color: rgb(21 21 21);
+    display: flex;
+    justify-content: center;
+}
+
+h1, h2, h3 {
+    color: var(--header-color);
+    text-align: center;
+}
+
+ul {
+    color: var(--text-color);
+    justify-content: center;
+    max-width: 1000px;
+}
+
+ul.recipes {
+    column-count: 3;
+}
+
+ul.tags {
+    column-count: 4;
+}
+
+li {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+}
+
+ul a:link {
+    color: var(--text-color);
+}
+
+ul a:hover {
+    text-decoration: none;
+}
+
+ul a:visited {
+    color: var(--visited-color);
+}
+