diff --git a/src/html/elements.rs b/src/html/elements.rs
new file mode 100644
index 0000000..e2ece82
--- /dev/null
+++ b/src/html/elements.rs
@@ -0,0 +1,151 @@
+use crate::html::Render;
+
+#[allow(non_camel_case_types)]
+pub(crate) struct h1 {
+ text: String,
+}
+
+impl Render for h1 {
+ fn render(&self) -> String {
+ format!("
{}
", self.text)
+ }
+}
+
+impl h1 {
+ pub(crate) fn new(text: String) -> Self {
+ Self { text }
+ }
+}
+
+#[allow(non_camel_case_types)]
+pub(crate) struct h2 {
+ text: String,
+}
+
+impl Render for h2 {
+ fn render(&self) -> String {
+ format!("{}
", self.text)
+ }
+}
+
+impl h2 {
+ pub(crate) fn new(text: String) -> Self {
+ Self { text }
+ }
+}
+
+#[allow(non_camel_case_types)]
+pub(crate) struct link {
+ rel: String,
+ href: String,
+}
+
+impl Render for link {
+ fn render(&self) -> String {
+ format!("", self.rel, self.href)
+ }
+}
+
+impl link {
+ pub(crate) fn new(rel: String, href: String) -> Self {
+ Self { rel, href }
+ }
+}
+
+#[allow(non_camel_case_types)]
+pub(crate) struct div {
+ id: String,
+ classes: Vec,
+ content: Vec>,
+}
+
+impl Render for div {
+ fn render(&self) -> String {
+ let classes = self.classes.join(" ");
+ format!(
+ "{}
",
+ self.id,
+ classes,
+ self.content.render()
+ )
+ }
+}
+
+impl div {
+ pub(crate) fn new(id: String, classes: Vec) -> Self {
+ Self {
+ id,
+ classes,
+ content: vec![],
+ }
+ }
+
+ pub(crate) fn append_element(&mut self, element: impl Render + 'static) {
+ self.content.push(Box::new(element));
+ }
+}
+
+#[allow(non_camel_case_types)]
+pub(crate) struct p {
+ id: String,
+ classes: Vec,
+ text: String
+}
+
+impl Render for p {
+ fn render(&self) -> String {
+ let classes = self.classes.join(" ");
+ format!("{}
", self.id, classes, self.text)
+ }
+}
+
+impl p {
+ pub(crate) fn new(id: String, classes: Vec, text: String) -> Self {
+ Self {
+ id,
+ classes,
+ text
+ }
+ }
+}
+
+#[allow(non_camel_case_types)]
+pub(crate) struct img {
+ id: String,
+ classes: Vec,
+ src: String,
+}
+
+impl Render for img {
+ fn render(&self) -> String {
+ let classes = self.classes.join(" ");
+ format!("
", self.id, classes, self.src)
+ }
+}
+
+impl img {
+ pub(crate) fn new(id: String, classes: Vec, src: String) -> Self {
+ Self { id, classes, src }
+ }
+}
+
+#[allow(non_camel_case_types)]
+pub(crate) struct a {
+ id: String,
+ classes: Vec,
+ href: String,
+ text: String,
+}
+
+impl Render for a {
+ fn render(&self) -> String {
+ let classes = self.classes.join(" ");
+ format!("{}", self.id, classes, self.href, self.text)
+ }
+}
+
+impl a {
+ pub(crate) fn new(id: String, classes: Vec, href: String, text: String) -> Self {
+ Self { id, classes, href, text }
+ }
+}
\ No newline at end of file
diff --git a/src/html/mod.rs b/src/html/mod.rs
new file mode 100644
index 0000000..80260ae
--- /dev/null
+++ b/src/html/mod.rs
@@ -0,0 +1,63 @@
+pub(crate) mod elements;
+
+pub(crate) trait Render {
+ fn render(&self) -> String;
+}
+
+pub(crate) struct Page {
+ title: String,
+ head: Vec>,
+ body: Vec>,
+}
+
+impl Render for Vec> {
+ fn render(&self) -> String {
+ let mut result = String::new();
+ for element in self {
+ let render = element.render();
+ result.push_str(&render);
+ }
+ result
+ }
+}
+
+impl Render for Page {
+ fn render(&self) -> String {
+ format!(
+ "\
+
+
+
+
+ {}
+
+{}
+
+
+{}
+
+",
+ self.title,
+ self.head.render(),
+ self.body.render()
+ )
+ }
+}
+
+impl Page {
+ pub(crate) fn new(title: String) -> Self {
+ Page {
+ title,
+ head: vec![],
+ body: vec![],
+ }
+ }
+
+ pub(crate) fn append_element_to_body(&mut self, element: impl Render + 'static) {
+ self.body.push(Box::new(element));
+ }
+
+ pub(crate) fn append_element_to_head(&mut self, element: impl Render + 'static) {
+ self.head.push(Box::new(element));
+ }
+}