home readme diff tree note docs

0commit 48f371d7b8f789ed2a4563054643fc868cb3c3fb
1Author: Mason Wright <mason@lakefox.net>
2Date:   Fri Nov 28 19:32:56 2025 -0700
3
4    Window object and remove complex attributes
5
6diff --git a/Makefile b/Makefile
7index 715686b..435bf3b 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -61,6 +61,9 @@ build/css_parser_playground: playground/css_parser.cc build/grim.o build/parser.
11 build/css_unit_playground: playground/css_unit.cc build/grim.o build/parser.o | build
12 	${CC} ${CCFLAGS} -Iinclude/ playground/css_unit.cc build/grim.o build/parser.o -o build/css_unit_playground
13 
14+build/window_playground: playground/window.cc build/grim.o build/parser.o | build
15+	${CC} ${CCFLAGS} -Iinclude/ playground/window.cc build/grim.o build/parser.o -o build/window_playground
16+
17 # --- Prebuild ---
18 
19 # use to prebuild the master.css
20diff --git a/README.txt b/README.txt
21index dc70472..d48b986 100644
22--- a/README.txt
23+++ b/README.txt
24@@ -1,4 +1,4 @@
25 Grim is a HTML/CSS render with a 'JS like' DOM API.
26 
27 Installation:
28-git submodule add http://decode.sh/grim.git
29+git submodule add http://git.ws/repo/grim.git
30diff --git a/include/grim.h b/include/grim.h
31index d61c21a..77af12c 100755
32--- a/include/grim.h
33+++ b/include/grim.h
34@@ -13,62 +13,107 @@
35 #include <stdexcept>
36 
37 // TODO:
38-// * Document object
39 // * templates
40 
41-class Attribute {
42-	private:
43-		std::string VALUE;
44-	public:
45-		Attribute(const std::string& value) : VALUE(value) {}
46+enum class UnitType : short {
47+	// Color
48+		TEST, HEX, RGB, RGBA, HSL,
49+	// Logic
50+		AND, OR, NOT, ONLY, COMMA,
51+	// Length 
52+		PX, EM, PERCENT, CM, CAP, CH, EX, IC, LH, RCAP, RCH, REM, REX, RIC, RLH, VH, 
53+		VW, VMAX, VMIN, VB, VI, CQW, CQH, CQI, CQB, CQMIN, CQMAX, MM, Q, IN, PC, PT,
54+	// Diminsion
55+		KHZ, HZ, SECOND, MS, FRACTION,
56+	// Angle
57+		DEG, RAD, GRAD, TURN,
58+	// Non-unit numbers
59+		NUMBER, INF, NEGINF, NaN, E, PI,
60 
61-		std::optional<std::string> String() const {return VALUE;}
62-		std::optional<int> Int() const {
63-			int iVal = 0;
64-			auto [ptr, ec] = std::from_chars(VALUE.data(), VALUE.data() + VALUE.size(), iVal);
65 
66-			if (ec == std::errc()) {
67-				return iVal;
68-			} else {
69-				return std::nullopt;
70-			}
71-		}
72 
73-		std::optional<double> Double() const {
74-			int dVal = 0.0;
75-			auto [ptr, ec] = std::from_chars(VALUE.data(), VALUE.data() + VALUE.size(), dVal);
76+	// Global
77+		INHERIT, INITIAL, REVERT, REVERT_LAYER, UNSET, AUTO, NONE,
78+	// Operator 
79+		EQ, LT, GT, LE, GE, ADD, SUB, MULT, DIV,
80+	// Baseline Position
81+		FIRST_BASELINE, LAST_BASELINE,
82+	// Box Edge
83+		CONTENT, PADDING, BORDER, MARGIN, FILL, STROKE, VIEW,
84+	// Distribution
85+		NORMAL, STRETCH, BETWEEN, AROUND, EVENLY,
86+	// Position
87+		CENTER, START, END, TOP, LEFT, BOTTOM, RIGHT, INLINE_START, INLINE_END, BLOCK_START, BLOCK_END, SELF_START, SELF_END, FLEX_START, FLEX_END,
88+	// Overflow
89+		CLIP, SCROLL,
90+		SAFE, UNSAFE,
91+	// Display
92+		HIDDEN, VISIBLE,  
93+	// Display Box
94+		CONTENTS, 
95+	// Display Inside
96+		FLOW, FLOW_ROOT, TABLE, FLEX, GRID, RUBY,
97+	// Display Internal
98+		TABLE_ROW_GROUP, TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_COLUMN_GROUP,
99+		TABLE_ROW, TABLE_CELL, TABLE_COLUMN, TABLE_CAPTION, RUBY_BASE, RUBY_TEXT, RUBY_BASE_CONTAINER,
100+		RUBY_TEXT_CONTAINER,
101+	// Display Legacy
102+		INLINE_BLOCK, INLINE_TABLE, INLINE_GRID, INLINE_FLEX,
103+	// Display List Item
104+		LISTITEM,
105+	// Display Outside
106+		BLOCK, INLINE, RUN_IN,
107+	// Shape
108+		RADIAL_SHAPE, CIRCLE, ELLIPSE,
109+	// Generic Family
110+		SERIF, SANS_SERIF, MONOSPACE, CURSIVE, FANTASY, SYSTEM_UI, UI_SERIF, UI_SANS_SERIF, UI_MONOSPACE, 
111+		UI_ROUNDED, MATH, FANGSONG,
112+	// Hue Interpolation
113+		SHORTER_HUE, LONGER_HUE, INCREASING_HUE, DECREASING_HUE, HUE,
114+	// Line Style
115+		DOTTED, DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET,
116+	// Image
117+		URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT,
118+	// Appearance																			 *Not added*
119+		MENU_LIST, TEXTFIELD, BASE_SELECT, BUTTON, CHECKBOX,
120+	// Space
121+		SPACE_BETWEEN, SPACE_AROUND, SPACE_EVENLY,
122+	// Background Attachment
123+		LOCAL, FIXED,
124+	// Background Blend Mode
125+		MULTIPLY, OVERLAY, DARKEN, LIGHTEN, COLOR_DODGE, COLOR_BURN, HARD_LIGHT, SOFT_LIGHT, DIFFERENCE, EXCLUSION, SATURATION, COLOR,  LUMINOSITY,
126+	// Background Clip
127+		BORDER_BOX, PADDING_BOX, CONTENT_BOX, TEXT, BORDER_AREA,
128+
129 
130-			if (ec == std::errc()) {
131-				return dVal;
132-			} else {
133-				return std::nullopt;
134-			}
135-		}
136+	// General Function
137+		CALC, MIN, BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
138+	// Easing Functions
139+		EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION, 
140+		STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH, 
141+	// Transform Functions
142+		MATRIX,	MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ, SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX, TRANSLATEY, TRANSLATEZ, 
143+		SKEW, SKEWX, SKEWY,
144 
145-		std::optional<bool> Bool() const {
146-			if (VALUE == "true") {
147-				return true;
148-			} else 
149-				if (VALUE == "false") {
150-					return false;
151-				} else {
152-					return std::nullopt;
153-				}
154-		}
155+	// Keyword
156+		PRINT, SCREEN, TOKEN, FUNC, GROUP, NO_SUPPORT, IMAGE, STRING
157+};
158 
159-		operator std::string() const { return VALUE; }
160-		operator int() const {
161-			if (auto val = Int()) { return *val; }
162-			throw std::runtime_error("Cannot convert '" + VALUE + "' to int.");
163-		}
164-		operator double() const {
165-			if (auto val = Double()) { return *val; }
166-			throw std::runtime_error("Cannot convert '" + VALUE + "' to double.");
167-		}
168-		operator bool() const {
169-			if (auto val = Bool()) { return *val; }
170-			throw std::runtime_error("Cannot convert '" + VALUE + "' to bool.");
171-		};
172+
173+// 4 bytes
174+union UnitValue {
175+	UnitType ENUM;
176+	int INT;
177+	unsigned int UINT;
178+	float FLOAT;
179+	bool BOOL;
180+	char CHAR;
181+};
182+
183+// 6 bytes 
184+struct Unit {
185+	UnitType type; // short
186+	UnitValue value;
187 };
188 
189 class Window;
190@@ -77,10 +122,11 @@ class Node {
191 	private:
192 		std::string TagName;
193 		std::unordered_map<std::string, std::string> attributes;
194-		Window* window;
195 	public:
196+		Window* window;
197+
198 		Node* parent;
199-		std::vector<std::unique_ptr<Node>> children;
200+		std::vector<Node> children;
201 		struct ClassList {
202 			private:
203 				Node* self;
204@@ -113,6 +159,7 @@ class Node {
205 			public:
206 				StyleList(Node* node) : self(node) {}
207 				std::string get(std::string key);
208+				std::vector<Unit> getUnit(std::string key);
209 				void set(std::string key, std::string value);
210 				void remove(std::string key);
211 				std::pair<std::string, std::string> item(size_t index);
212@@ -131,7 +178,7 @@ class Node {
213 
214 		void setAttribute(const std::string& name, const std::string& value);
215 		void deleteAttribute(const std::string& name);
216-		std::optional<Attribute> getAttribute(const std::string& name) const;
217+		std::string getAttribute(const std::string& name) const;
218 
219 		const std::unordered_map<std::string, std::string>& getAttributes() const;
220 		std::vector<std::string> getAttributeKeys();
221@@ -217,118 +264,12 @@ enum class KeyType : short {
222 	Z_INDEX, ZOOM
223 };
224 
225-enum class UnitType : short {
226-	// Color
227-		TEST, HEX, RGB, RGBA, HSL,
228-	// Logic
229-		AND, OR, NOT, ONLY, COMMA,
230-	// Length 
231-		PX, EM, PERCENT, CM, CAP, CH, EX, IC, LH, RCAP, RCH, REM, REX, RIC, RLH, VH, 
232-		VW, VMAX, VMIN, VB, VI, CQW, CQH, CQI, CQB, CQMIN, CQMAX, MM, Q, IN, PC, PT,
233-	// Diminsion
234-		KHZ, HZ, SECOND, MS, FRACTION,
235-	// Angle
236-		DEG, RAD, GRAD, TURN,
237-	// Non-unit numbers
238-		NUMBER, INF, NEGINF, NaN, E, PI,
239-
240-
241-
242-	// Global
243-		INHERIT, INITIAL, REVERT, REVERT_LAYER, UNSET, AUTO, NONE,
244-	// Operator 
245-		EQ, LT, GT, LE, GE, ADD, SUB, MULT, DIV,
246-	// Baseline Position
247-		FIRST_BASELINE, LAST_BASELINE,
248-	// Box Edge
249-		CONTENT, PADDING, BORDER, MARGIN, FILL, STROKE, VIEW,
250-	// Distribution
251-		NORMAL, STRETCH, BETWEEN, AROUND, EVENLY,
252-	// Position
253-		CENTER, START, END, TOP, LEFT, BOTTOM, RIGHT, INLINE_START, INLINE_END, BLOCK_START, BLOCK_END, SELF_START, SELF_END, FLEX_START, FLEX_END,
254-	// Overflow
255-		CLIP, SCROLL,
256-		SAFE, UNSAFE,
257-	// Display
258-		HIDDEN, VISIBLE,  
259-	// Display Box
260-		CONTENTS, 
261-	// Display Inside
262-		FLOW, FLOW_ROOT, TABLE, FLEX, GRID, RUBY,
263-	// Display Internal
264-		TABLE_ROW_GROUP, TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_COLUMN_GROUP,
265-		TABLE_ROW, TABLE_CELL, TABLE_COLUMN, TABLE_CAPTION, RUBY_BASE, RUBY_TEXT, RUBY_BASE_CONTAINER,
266-		RUBY_TEXT_CONTAINER,
267-	// Display Legacy
268-		INLINE_BLOCK, INLINE_TABLE, INLINE_GRID, INLINE_FLEX,
269-	// Display List Item
270-		LISTITEM,
271-	// Display Outside
272-		BLOCK, INLINE, RUN_IN,
273-	// Shape
274-		RADIAL_SHAPE, CIRCLE, ELLIPSE,
275-	// Generic Family
276-		SERIF, SANS_SERIF, MONOSPACE, CURSIVE, FANTASY, SYSTEM_UI, UI_SERIF, UI_SANS_SERIF, UI_MONOSPACE, 
277-		UI_ROUNDED, MATH, FANGSONG,
278-	// Hue Interpolation
279-		SHORTER_HUE, LONGER_HUE, INCREASING_HUE, DECREASING_HUE, HUE,
280-	// Line Style
281-		DOTTED, DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET,
282-	// Image
283-		URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT,
284-	// Appearance																			 *Not added*
285-		MENU_LIST, TEXTFIELD, BASE_SELECT, BUTTON, CHECKBOX,
286-	// Space
287-		SPACE_BETWEEN, SPACE_AROUND, SPACE_EVENLY,
288-	// Background Attachment
289-		LOCAL, FIXED,
290-	// Background Blend Mode
291-		MULTIPLY, OVERLAY, DARKEN, LIGHTEN, COLOR_DODGE, COLOR_BURN, HARD_LIGHT, SOFT_LIGHT, DIFFERENCE, EXCLUSION, SATURATION, COLOR,  LUMINOSITY,
292-	// Background Clip
293-		BORDER_BOX, PADDING_BOX, CONTENT_BOX, TEXT, BORDER_AREA,
294-
295-
296-	// General Function
297-		CALC, MIN, BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
298-	// Easing Functions
299-		EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION, 
300-		STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH, 
301-	// Transform Functions
302-		MATRIX,	MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ, SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX, TRANSLATEY, TRANSLATEZ, 
303-		SKEW, SKEWX, SKEWY,
304-
305-	// Keyword
306-		PRINT, SCREEN, TOKEN, FUNC, GROUP, NO_SUPPORT, IMAGE, STRING
307-};
308-
309-// 4 bytes
310-union UnitValue {
311-	UnitType ENUM;
312-	int INT;
313-	unsigned int UINT;
314-	float FLOAT;
315-	bool BOOL;
316-	char CHAR;
317-};
318-
319-// 6 bytes 
320-struct Unit {
321-	UnitType type; // short
322-	UnitValue value;
323-};
324-
325-enum class StyleType : char {
326-	CONTAINER,  COUNTER,    FONTFACE,  FONTFEATURE,  SWASH,  ANNOTATION,  ORNAMENTS,  STYLISTIC,  STYLESET,  CHARACTERVARIANT,  FONTPALETTEVALUES,
327-	IMPORT,     KEYFRAMES,  LAYER,     NAMESPACE,    PAGE,   PROPERTY,    ROOT,       SHEET,      MEDIA,     STARTINGSTYLE,     STYLE
328-};
329 
330 class Style {
331 	public:
332 		Window* window;
333-
334 		std::unordered_map<std::string, std::vector<size_t>> basemap{};
335 
336-		StyleType type;
337 		std::string name;
338 		std::vector<std::vector<std::string>> selector{};
339 		
340@@ -402,8 +343,7 @@ class Window {
341 
342 	public:
343 		
344-		void loadHTML(std::istream& inputStream);
345-		void loadCSS(std::istream& inputStream);
346+		void parseHTML(std::istream& inputStream);
347 		
348 		Node document;
349 		Style CSS;
350@@ -478,4 +418,12 @@ class Window {
351 		}
352 };
353 
354+// Will need to require an adapter later
355+// for now I just want to work on at rules
356+
357+/*
358+For the adapter, it will take a window as a argument to set the adapters capabilties
359+*/
360+Window createWindow();
361+
362 #endif // NODE_H
363diff --git a/include/parser.h b/include/parser.h
364index aca9526..ba33470 100755
365--- a/include/parser.h
366+++ b/include/parser.h
367@@ -11,11 +11,15 @@
368 class Node;
369 class Style;
370 
371+struct ParsedDocument {
372+	Node document;
373+	Style CSS;
374+};
375+
376 // Declare the parsing functions
377 std::unordered_map<std::string, std::string> parseAttributes(std::string_view& token);
378-Node parseHTML(std::istream& inputStream);
379+ParsedDocument parseDocument(std::istream& inputStream);
380 Style parseCSS(std::istream& inputStream);
381-std::unordered_map<KeyType, std::vector<Unit>> parseCSSInline(std::string inlineStyles);
382 std::vector<Unit> getUnit(std::string);
383 
384 #endif
385diff --git a/playground/css_parser.cc b/playground/css_parser.cc
386index 57bde4c..8adeeec 100644
387--- a/playground/css_parser.cc
388+++ b/playground/css_parser.cc
389@@ -6,56 +6,6 @@
390 
391 int main() {
392 	std::string cssContent = R"~~~(
393-h1 {
394-	color: red;
395-}
396-
397-h1 > #id {
398-	background: purple;
399-}
400-
401-div:checked {
402-	display: flex;
403-	margin: 1px 100% 20vh;
404-}
405-
406-
407-html#nested {
408-	background: url("https://example.com");
409-
410-	h1[value="nested"] {
411-		color: red;	
412-	}
413-
414-	&.class {
415-		border:
416-		none;
417-	}
418-}
419-.notice {
420-  width: 90%;
421-  justify-content: center;
422-  border-radius: 1rem;
423-  border: black solid 2px;
424-  background-color: #ffc107;
425-  color: black;
426-  padding: 1rem;
427-  .notice-heading::before {
428-    /* equivalent to `.notice .notice-heading::before` */
429-    content: "i ";
430-  }
431-  font-size: 10px;
432-  &.warning {
433-    /* equivalent to `.notice.warning` */
434-    background-color: #d81b60;
435-    border-color: #d81b60;
436-    color: white;
437-    .warning-heading::before {
438-      /* equivalent to `.notice.warning .warning-heading::before` */
439-      content: "! ";
440-    }
441-  }
442-}
443 @media (hover: hover) {
444   abbr:hover {
445     color: #001ca8;
446@@ -68,5 +18,5 @@ html#nested {
447 
448 	Style style = parseCSS(inputFile);
449 
450-	Style s = style.children[4];
451+	Style s = style.children[0];
452 }
453diff --git a/playground/parseselectorparts.cc b/playground/parseselectorparts.cc
454index ba0065d..e0e9299 100644
455--- a/playground/parseselectorparts.cc
456+++ b/playground/parseselectorparts.cc
457@@ -5,8 +5,8 @@
458 #include <iostream>
459 
460 int main() {
461-	std::vector<std::vector<std::string>> parts = parseSelectorParts("2n + 3");
462+	std::vector<std::vector<std::string>> parts = parseSelectorParts("h1:test, h1[name='asd']");
463 
464-	std::cout << std::stoi(parts[0][0].substr(0,parts[0][0].length()-1)) << std::endl;
465+	std::cout << parts[1][1] << std::endl;
466 }
467 
468diff --git a/playground/window.cc b/playground/window.cc
469new file mode 100644
470index 0000000..bbf274a
471--- /dev/null
472+++ b/playground/window.cc
473@@ -0,0 +1,45 @@
474+#include "../include/grim.h"
475+#include "../include/parser.h" 
476+#include <string>
477+#include <sstream>
478+#include <vector>
479+#include <iostream>
480+
481+int main() {
482+	std::string html = "<!DOCTYPE html>"
483+			"<html>"
484+				"<head>"
485+					"<title>This is a simple test</title>"
486+					"<!--<link rel=\"stylesheet\" href=\'/style.css\'/>-->"
487+					"<style>h1 {background: red;}</style>"
488+				"</head>"
489+				"<body>"
490+					"<h1 id=\"h1Tag\">Header</h1>"
491+					"<p id=\"paragraph\" class=\"class1 class2\">This is some text for a simple test</p>"
492+					"<div contenteditable=\"true\">"
493+						"<p>this is a only child</p>"
494+					"</div>"
495+					"<input type=\"text\" checked required/>"
496+					"<ul>"
497+						"<li>one</li>"
498+						"<li>two</li>"
499+						"<li>three</li>"
500+						"<li>four</li>"
501+					"</ul>"
502+					"<input type=\"radio\" checked required/>"
503+					"<input type=\"text\" readonly/>"
504+					"<input type=\"text\" disabled/>"
505+					"<textarea></textarea>"
506+					"<textarea readonly></textarea>"
507+					"<textarea disabled></textarea>"
508+				"</body>"
509+			"</html>";
510+
511+
512+	std::stringstream ss(html);
513+
514+	Window window = createWindow();
515+	window.parseHTML(ss);
516+
517+	Node document = window.document;
518+}
519diff --git a/src/grim.cc b/src/grim.cc
520index 070f139..fd316a0 100755
521--- a/src/grim.cc
522+++ b/src/grim.cc
523@@ -16,7 +16,7 @@
524 // |(TOC) Table of contents                               |
525 // +------------------------------------------------------+
526 // |(p98ym) Utils                                         |
527-// | (hldfk) Document Parsing                             |
528+// |(hldfk) Window                                        |
529 // |(tigfn) Attribute Handling                            |
530 // |(f98y0) ClassList                                     |
531 // |(lhujn) StyleList                                     |
532@@ -66,18 +66,21 @@ void trimSpace(std::string& str) {
533     }
534 }
535 // +---------------------------------------------------+
536-// | (hldfk) Document Parsing                          |
537+// | (hldfk) Window                                    |
538 // +---------------------------------------------------+
539-
540-void Window::loadHTML(std::istream& inputStream) {
541-	document = parseHTML(inputStream);	
542+// All of windows props will come from the adapter setting
543+// them
544+Window createWindow() {
545+	Window window;
546+	return window;
547 }
548 
549-void Window::loadCSS(std::istream& inputStream) {
550-	CSS.addChild(parseCSS(inputStream));
551+void Window::parseHTML(std::istream& inputStream) {
552+	ParsedDocument doc = parseDocument(inputStream);
553+	document = doc.document;
554+	CSS = doc.CSS;
555 }
556 
557-
558 // +---------------------------------------------------+
559 // | (tigfn) Attribute Handling                        |
560 // +---------------------------------------------------+
561@@ -110,20 +113,20 @@ void Node::deleteAttribute(const std::string& name) {
562 	attributes.erase(name);
563 }
564 
565-std::optional<Attribute> Node::getAttribute(const std::string& name) const {
566+std::string Node::getAttribute(const std::string& name) const {
567 	// First check in the user set attributes
568 	auto it = attributes.find(name);
569 	if (it != attributes.end()) {
570 		// return the value wrapped in the Attribute class
571-		return Attribute(it->second);
572+		return it->second;
573 	} else {
574 		// If its not set by the user then find it in the
575 		// default values map
576 		it = DEFAULT_ATTRIBUTE_VALUES.find(name);
577 		if (it != DEFAULT_ATTRIBUTE_VALUES.end()) {
578-			return Attribute(it->second);
579+			return it->second;
580 		} else {
581-			return std::nullopt;
582+			return "";
583 		}
584 	}
585 }
586@@ -294,6 +297,9 @@ void Node::ClassList::toggle(std::string value) {
587 // contain the true value of the inline style and still have an
588 // easy to use API for .style. To get all styles, call .length()
589 // and itterate through all items using .item(<size_t>).
590+//
591+// getUnit is a interal method to get styles from the stylelist
592+// as parsed units for rendering
593 
594 void Node::StyleList::createIndex() {
595 	std::string styles = self->attributes["style"];
596@@ -414,6 +420,27 @@ std::string Node::StyleList::get(std::string key) {
597 	return "";
598 }
599 
600+std::vector<Unit> Node::StyleList::getUnit(std::string key) {
601+	if (!checkIndex()) {
602+		createIndex();
603+	}
604+	std::string styles = self->attributes["style"];
605+	for (size_t i = 0; i < indexes.size(); i++) {
606+		int f = std::get<0>(indexes[i]);
607+		int s = std::get<1>(indexes[i]);
608+		std::string slicedKey = styles.substr(f, s-f);
609+		trimSpace(slicedKey);
610+		if (slicedKey == key) {
611+			int t = std::get<2>(indexes[i]);
612+			std::string value = styles.substr(s+1, t-(s+1));
613+			trimSpace(value);
614+			return getUnit(value);
615+		}
616+	}
617+	std::vector<Unit> empty;
618+	return empty;
619+}
620+
621 void Node::StyleList::set(std::string key, std::string value) {
622 	if (!checkIndex()) {
623 		createIndex();
624@@ -470,49 +497,49 @@ void Node::StyleList::remove(std::string key) {
625 // +---------------------------------------------------+
626 
627 Node* Node::createElement(std::string name) {
628-    std::unique_ptr<Node> newNode = std::make_unique<Node>();
629-    newNode->setTagName(name);
630-    newNode->parent = this;
631-    children.push_back(std::move(newNode));
632-    return children.back().get();
633+	Node newNode;
634+	newNode.setTagName(name);
635+	newNode.parent = this;
636+	children.push_back(newNode);
637+	return &children.back();
638 }
639 
640 std::string Node::print(int indent) {
641 	std::string out = "";
642-    for (int i = 0; i < indent; ++i) {
643-        out += "  ";
644-    }
645+	for (int i = 0; i < indent; ++i) {
646+		out += "  ";
647+	}
648 
649-    out += "<" + getTagName();
650-    for (const auto& attr_pair : getAttributes()) {
651-        if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
652-            continue;
653-        }
654-        out += " " + attr_pair.first;
655-        if (!attr_pair.second.empty()) {
656-            out += "=\"" + attr_pair.second + "\"";
657-        }
658-    }
659-    out += ">";
660+	out += "<" + getTagName();
661+	for (const auto& attr_pair : getAttributes()) {
662+		if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
663+			continue;
664+		}
665+		out += " " + attr_pair.first;
666+		if (!attr_pair.second.empty()) {
667+			out += "=\"" + attr_pair.second + "\"";
668+		}
669+	}
670+	out += ">";
671 
672-    if (auto innerText = getAttribute("innerText")) {
673-	    if (innerText != std::nullopt) {
674-		    out += "\n" + *innerText->String();
675-	    }
676-    }
677+	std::string innerText = getAttribute("innerText"); 
678 
679-    out += "\n";
680+	if (innerText != "") {
681+		out += "\n" + innerText;
682+	}
683 
684-    for (const auto& child : children) {
685-        out += child->print(indent + 1)+"\n";
686-    }
687+	out += "\n";
688 
689-    for (int i = 0; i < indent; ++i) {
690-        out += "  ";
691-    }
692-    out += "</" + getTagName() + ">\n";
693+	for (auto& child : children) {
694+		out += child.print(indent + 1)+"\n";
695+	}
696+
697+	for (int i = 0; i < indent; ++i) {
698+		out += "  ";
699+	}
700+	out += "</" + getTagName() + ">\n";
701 
702-    return out;
703+	return out;
704 }
705 
706 // +---------------------------------------------------+
707@@ -665,8 +692,8 @@ std::vector<std::string> parseNodeParts(Node* node) {
708 	parts.push_back(node->getTagName());
709 	auto id = node->getAttribute("id");
710 
711-	if (id != std::nullopt) {
712-		parts.push_back("#"+*id->String());
713+	if (id != "") {
714+		parts.push_back("#"+id);
715 	}
716 
717 	// classes and attributes
718@@ -677,8 +704,8 @@ std::vector<std::string> parseNodeParts(Node* node) {
719 			std::string v;
720 			auto av = node->getAttribute(k);
721 
722-			if (av != std::nullopt) {
723-				v = "=\""+*av->String()+"\"";
724+			if (av != "") {
725+				v = "=\""+av+"\"";
726 
727 			}
728 			parts.push_back('['+k+v+']');
729@@ -708,13 +735,15 @@ void Style::addChild(Style s) {
730 	// Get the last selector group from the parsed selector parts
731 	// because that is the selector that applies to an element
732 	// and map them into basemap
733-	std::vector<std::string> targetParts = s.selector[s.selector.size()-1];
734-	for (auto p : targetParts) {
735-		if (basemap.find(p) != basemap.end()) {
736-			basemap[p].push_back(index);
737-		} else {
738-			std::vector<size_t> bm = {index};
739-			basemap[p] = bm;
740+	if (s.selector.size() > 0) {
741+		std::vector<std::string> targetParts = s.selector[s.selector.size()-1];
742+		for (auto p : targetParts) {
743+			if (basemap.find(p) != basemap.end()) {
744+				basemap[p].push_back(index);
745+			} else {
746+				std::vector<size_t> bm = {index};
747+				basemap[p] = bm;
748+			}
749 		}
750 	}
751 }
752@@ -751,35 +780,35 @@ std::unordered_map<KeyType, UnitType> Style::getStyles(Node* node) {
753 
754 	for (size_t c : canidates) {
755 		// need to check the type of sheet it is
756-		SheetType t = sheet->type;
757 		std::vector<std::vector<std::string>> selector = sheet->children[c].selector;
758 
759-		if (t == SheetType::STYLE) {
760-			// Run testSelector with the preparsed parts
761-			if (testSelector(node, selector)) {
762-				std::unordered_map<std::string, std::string> props = sheet->children[c].properties;
763-				for (auto p : props) {
764-					styles->insert(p);
765-				}
766-
767-				// If the Sheet matches then we will repeat the same process with that sheet
768-				if (sheet->children[c].children.size() > 0) {
769-					getstyles(node, node_parts, &sheet->children[c], styles);
770-				}
771+		// Run testSelector with the preparsed parts
772+		if (testSelector(node, selector)) {
773+			std::unordered_map<std::string, std::string> props = sheet->children[c].properties;
774+			for (auto p : props) {
775+				styles->insert(p);
776 			}
777-		} else if (t == SheetType::CONTAINER) {
778 
779+			// If the Sheet matches then we will repeat the same process with that sheet
780+			if (sheet->children[c].children.size() > 0) {
781+				getstyles(node, node_parts, &sheet->children[c], styles);
782+			}
783 		}
784 	}*/
785 }
786 
787-// Used for root node without a parent
788-const std::vector<std::unique_ptr<Node>> EMPTY_NODE_CHILDREN_VECTOR;
789+// +------------------------------------------------------+
790+// |(7tygl) Query matching 				  |
791+// +------------------------------------------------------+
792+// testQuery tries to calculate if the query (ie a media query or 
793+// container query) applies to the node passed
794+
795+bool testQuery(Node* node, std::vector<std::string> parts) {
796+	return true;	
797+}
798 
799-/*
800-at-rules need to be calculated at getstyles
801-make a function to testAtRules
802-*/
803+// Used for root node without a parent
804+std::vector<Node> EMPTY_NODE_CHILDREN_VECTOR;
805 
806 // node: The node you are checking the selector against
807 // selector: output of parseSelector
808@@ -869,19 +898,19 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
809 	// the children variable is the PARENTS CHILDREN not the current node's children
810 	// aka siblings
811 	// if you want those, node->children
812-	const std::vector<std::unique_ptr<Node>>& children = (node->parent != nullptr) ? node->parent->children : EMPTY_NODE_CHILDREN_VECTOR;
813+	std::vector<Node>& children = (node->parent != nullptr) ? node->parent->children : EMPTY_NODE_CHILDREN_VECTOR;
814 	size_t childIndex = 0;
815 	size_t childTypeIndex = 0;
816 	size_t childTypeCount = 0;
817 	if (children.size() > 1) {
818 		for (size_t i = 0; i < children.size(); i++) {
819-			Node* child_ptr = children[i].get();
820+			Node child = children[i];
821 			
822-			if (child_ptr == node) {
823+			if (&child == node) {
824 				childIndex = i;	
825 				childTypeIndex = childTypeCount;
826 			}
827-			if (child_ptr->getTagName() == tagName) {
828+			if (child.getTagName() == tagName) {
829 				childTypeCount++;
830 			}
831 		}
832@@ -948,9 +977,11 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
833 	for (auto p : parts) {
834 		if (p[0] == '*') {
835 			matched = true;
836-		} else 
837-		if (i == 0 && p[0] != '#' && p[0] != '.' &&  p[0] != '[' && p[0] != ':') {
838+		} else if (i == 0 && p[0] != '#' && p[0] != '.' &&  p[0] != '[' && p[0] != ':') {
839 			matched = tagName == p;
840+		} else if (p[0] == '@') {
841+			// This is a at-rule we will process it in a different function
842+			return testQuery(node, parts);
843 		} else if (p[0] == '#') {
844 			matched = "#"+attributes["id"] == p;
845 		} else if (p[0] == '.') {
846@@ -1027,8 +1058,8 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
847 						// The child combinator only checks direct children so we 
848 						// can loop through and if we find one that matches ip[1:]
849 						// (remove the ">") we can stop and return
850-						for (const auto& c : node->children) {
851-							found = testSelector(c.get(), slicedParts);
852+						for (Node c : node->children) {
853+							found = testSelector(&c, slicedParts);
854 							if (found) {
855 								break;
856 							}
857@@ -1038,7 +1069,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
858 							// If there isn't a prevous sibling then it cannot match
859 							if (childIndex < children.size()-1) {
860 								// In this selector we just need to get the direct sibling and test it
861-								found = testSelector(children[childIndex+1].get(), slicedParts);
862+								found = testSelector(&children[childIndex+1], slicedParts);
863 							} else {
864 								// if the element is the last there is no way it could have one
865 								found = false;
866@@ -1049,7 +1080,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
867 							for (size_t i = childIndex+1; i < children.size(); i++) {
868 								// We have to test every selector after the current element,
869 								// if any match then it matches
870-								found = testSelector(children[i].get(), slicedParts);
871+								found = testSelector(&children[i], slicedParts);
872 								if (found) {
873 									break;
874 								}
875@@ -1076,11 +1107,10 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
876 								break;
877 							}
878 							
879-							for (const auto& child_ptr : n->children) {
880-								Node* child = child_ptr.get();
881-								if (visited.find(child) == visited.end()) {
882-									visited.insert(child);
883-									queue.push(child);
884+							for (Node child : n->children) {
885+								if (visited.find(&child) == visited.end()) {
886+									visited.insert(&child);
887+									queue.push(&child);
888 								}
889 							}	
890 						}
891@@ -1236,9 +1266,9 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
892 						if (last) {
893 							// Reverse through the siblings
894 							for (size_t i = children.size() - 1; i >= 0; i--) {
895-								Node* child_ptr = children[i].get();
896-								if (child_ptr != node) {
897-									if (testSelector(child_ptr, pOS)) {
898+								Node child = children[i];
899+								if (&child != node) {
900+									if (testSelector(&child, pOS)) {
901 										// if the selector matches the ofSelector add one
902 										index++;
903 									}
904@@ -1252,9 +1282,9 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
905 							}
906 						} else {
907 							for (size_t i = 0; i < children.size(); i++) {
908-								Node* child_ptr = children[i].get();
909-								if (child_ptr != node) {
910-									if (testSelector(child_ptr, pOS)) {
911+								Node child = children[i];
912+								if (&child != node) {
913+									if (testSelector(&child, pOS)) {
914 										// if the selector matches the ofSelector add one
915 										index++;
916 									}
917@@ -1352,7 +1382,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
918 				return false;
919 			} else {
920 				// In this selector we just need to get the direct sibling and test it
921-				matched = testSelector(children[childIndex-1].get(), {trimmed});
922+				matched = testSelector(&children[childIndex-1], {trimmed});
923 			}
924 		}
925 	} else if (symbol == "~") {
926@@ -1361,7 +1391,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
927 			for (size_t i = 0; i < childIndex; i++) {
928 				// We have to test every selector before the current element,
929 				// if any match then it matches
930-				matched = testSelector(children[i].get(), {trimmed});
931+				matched = testSelector(&children[i], {trimmed});
932 				if (matched) {
933 					break;
934 				}
935@@ -1387,24 +1417,3 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
936 	return matched;
937 }
938 
939-// +------------------------------------------------------+
940-// |(7tygl) Query matching 				  |
941-// +------------------------------------------------------+
942-// testQuery takes any sheet class that isn't a SheetType::STYLE
943-// and tries to calculate if the query (ie a media query or 
944-// container query) applies to the node passed
945-/*
946-bool testQuery(Sheet* query, Node* node, Window* window) {
947-	// First step is to understand the query
948-	// it is parsed from parseSelector parts
949-	// @container scroll-state(scrollable: top) 
950-	// {{" ", "scroll-state(scrollable: top)"}}
951-
952-	//I feel like having string content is going to make things slow
953-
954-
955-	if (query->type == SheetType::CONTAINER) {
956-		
957-	}	
958-}
959-*/
960diff --git a/src/parser.cc b/src/parser.cc
961index 487e591..c1dbd83 100755
962--- a/src/parser.cc
963+++ b/src/parser.cc
964@@ -107,239 +107,6 @@ std::unordered_map<std::string, std::string> parseAttributes(std::string_view to
965 	return attrs;
966 }
967 
968-Node parseHTML(std::istream& inputStream) {
969-	Node root;
970-	root.setTagName("root");
971-
972-	Node* currentNode = &root;
973-
974-	bool inTag = false;
975-	bool escaped = false;
976-	bool inQuote = false;
977-	bool inComment = false;
978-	bool waitToClose = false;
979-	char quoteType = '"';
980-
981-	std::string token;
982-
983-	char current;
984-	while (inputStream.get(current)) {
985-		// Finds the --> and removes it then resets inComment
986-		if (inComment) {
987-			// added the peek to prevent hitting on every -
988-			if (current == '-' && inputStream.peek() == '-') {
989-				char a,b;
990-				// load the next two
991-				if (inputStream.get(a) && inputStream.get(b)) {
992-					// We know a == -
993-					if (b == '>') {
994-						// Close the comment
995-						inComment = false;
996-					}
997-				}
998-
999-				if (!inComment) {
1000-					// we don't put anything back and that puts us where we need to be
1001-					continue;
1002-				} else {
1003-					// Not the end 
1004-					inputStream.putback(b);
1005-					inputStream.putback(a);
1006-				}
1007-			}
1008-			continue;
1009-		}
1010-
1011-		std::string tagName = currentNode->getTagName();
1012-		if (!waitToClose && (
1013-					tagName == "style" || 
1014-					tagName == "script" ||
1015-					tagName == "textarea" 
1016-					) && current == '<' && inputStream.peek() == '/' ) {
1017-			// If its the starting of a closing tag and we are inside of a special tag
1018-			// we need to see if the upcoming closing tag is the correct closing tag
1019-			// if it is then we put the read ahead back and waitToClose. This will let normal parsing
1020-			// continue
1021-			const size_t readTo = tagName.length()+2;
1022-			std::string buffer(readTo, '\0');
1023-
1024-			inputStream.read(&buffer[0], readTo);
1025-
1026-			if (buffer == "/"+tagName+">") {
1027-				waitToClose = true;
1028-			} else {
1029-				token += current;
1030-			}
1031-
1032-			for (int i = readTo-1; i>= 0; i--) {
1033-				inputStream.putback(buffer[i]);
1034-			}
1035-			if (!waitToClose) {
1036-				continue;
1037-			}
1038-		} else if (!waitToClose && (
1039-					tagName == "style" || 
1040-					tagName == "script" ||
1041-					tagName == "textarea" 
1042-					)) {
1043-			token += current;
1044-			continue;
1045-		}
1046-
1047-		if (inTag) {
1048-			 if ((current == '"' || current == '\'') && !escaped) {
1049-				if (inQuote && current == quoteType) {
1050-					inQuote = false;
1051-				} else if (!inQuote) {
1052-					inQuote = true;
1053-					quoteType = current;
1054-				}
1055-			}
1056-
1057-			if (current == '>' && !inQuote && !escaped) {
1058-				inTag = false;
1059-				waitToClose = false;
1060-				// Even if the next tag is next still add a text tag as we can just check if its empty and remove it
1061-				// this check would still have to be done either way so we aren't wasting anything
1062-				if (token.length() > 0) {
1063-					bool empty = true;
1064-					bool closingTag = false;
1065-					for (size_t i = 0; i < token.length(); i++) {
1066-						auto c = token[i];
1067-						if (std::isspace(c)) {
1068-							continue;
1069-						} else if (c == '/') {
1070-							closingTag = true;
1071-						} else {
1072-							empty = false;
1073-							break;
1074-						}
1075-					}
1076-
1077-					bool selfClosing = false;
1078-					int selfClosingPosition = token.length()-1;
1079-					if (!closingTag) {
1080-						for (int i = token.length()-1; i >= 0; i--) {
1081-							auto c = token[i];
1082-							if (std::isspace(c)) {
1083-								continue;
1084-							} else if (c == '/') {
1085-								selfClosingPosition = i;
1086-								selfClosing = true;
1087-								break;
1088-							} else {
1089-								break;
1090-							}
1091-						}
1092-					}
1093-
1094-					if (!empty) {
1095-						if (selfClosing) {
1096-							//Create element and don't jump inside
1097-							// Use node instead of currentNode because we do not need to jump into the created element
1098-							auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
1099-							auto node = currentNode->createElement(attrs["tagName"]);
1100-							for (auto const& pair : attrs) {
1101-								// All attributes are stored as strings so we can just throw them in
1102-								node->setAttribute(pair.first, pair.second);
1103-							}
1104-						} else if (closingTag) {
1105-							// Checksum and tree move
1106-							std::string tagName = "";
1107-							for (auto t : token) {
1108-								if (!std::isspace(t) && t != '/') {
1109-									tagName += t;
1110-								} else if (t == '/') {
1111-									// For closing tags we just want the name
1112-									continue;
1113-								} else if (tagName.length() > 0) {
1114-									break;
1115-								}
1116-							}
1117-							if (currentNode->getTagName() == tagName) {
1118-								currentNode = currentNode->parent;
1119-							} else {
1120-								std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
1121-							}
1122-						} else {
1123-							// Create a element and jump inside
1124-							auto attrs = parseAttributes(token);
1125-							// Don't add elements like <!DOCTYPE>
1126-							if (attrs["tagName"][0] != '!') {
1127-								currentNode = currentNode->createElement(attrs["tagName"]);
1128-								for (auto const& pair : attrs) {
1129-									// All attributes are stored as strings so we can just throw them in
1130-									currentNode->setAttribute(pair.first, pair.second);
1131-								}
1132-							}
1133-						}
1134-					}
1135-				}
1136-
1137-				token = "";	
1138-				continue;
1139-			}
1140-		} else if (!escaped && current == '<') {
1141-			// if the next character is a !
1142-			if (inputStream.peek() == '!') {
1143-				char a,b,c;
1144-				// load the next three characters (includes !)
1145-				if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
1146-					// We know a == !
1147-					if (b == '-' && c == '-') {
1148-						inComment = true;
1149-					}
1150-				}
1151-
1152-				if (inComment) {
1153-					continue;
1154-				} else {
1155-					// Not a comment add all back
1156-					inputStream.putback(c);
1157-					inputStream.putback(b);
1158-					inputStream.putback(a);
1159-				}
1160-			}
1161-			inTag = true;
1162-			
1163-			// Here's where you actually make the text node and above the real nodes
1164-			// can also prob make the vector of tokens a single variable
1165-			// it really just needs to be a data string bc we know the type based on inTag
1166-
1167-
1168-			bool hasText = false;
1169-			for (auto t : token) {
1170-				if (!std::isspace(t)) {
1171-					hasText = true;
1172-					break;
1173-				}
1174-			}
1175-
1176-			// TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
1177-			// | Will need something similar for innerHTML
1178-			if (hasText) {
1179-				auto node = currentNode->createElement("text");
1180-				node->setAttribute("innerText", token);
1181-			}
1182-
1183-
1184-			token = "";
1185-			continue;
1186-		}
1187-		
1188-		escaped = false;
1189-
1190-		if (current == '\\') { //'
1191-			escaped = true;
1192-			continue;
1193-		}
1194-
1195-		token += current;
1196-	}
1197-
1198-	return root;
1199-}
1200-
1201 static const std::unordered_map<std::string, KeyType> keySearch {
1202 	{"accent-color", KeyType::ACCENT_COLOR},
1203 	{"align-content", KeyType::ALIGN_CONTENT},
1204@@ -1488,71 +1255,6 @@ Style parseCSS(std::istream& inputStream) {
1205 
1206 			current = &current->children.back();
1207 
1208-			// here we need to take the selector (buffer)
1209-			// and find out what type it is. Also need to manage the nesting
1210-			if (parts[0][0][0] == '@') {
1211-				if (parts[0][0] == "@container") {
1212-					current->type = StyleType::CONTAINER;
1213-					bool hasName = false;
1214-					if (parts[0].size() >= 3) {
1215-						// @container not (width < 400px)
1216-						if (parts[0][2] != "not") {
1217-							// @container (width > 400px) or|and (height > 400px)
1218-							if (parts[0].size() >= 5) {
1219-								if (parts[0][5] != "and" || parts[0][5] != "or") {
1220-									current->name = parts[0][2];
1221-									hasName = true;
1222-								}
1223-							}
1224-						}
1225-					}
1226-					// If a name is present then remove the name and tag from the selector
1227-					// else just remove the @container
1228-					if (hasName) {
1229-						current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 3, current->selector[0].end());
1230-					} else {
1231-						current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 1, current->selector[0].end());
1232-					}
1233-				} else if (parts[0][0] == "@counter-style") {
1234-					current->name = parts[0][2];
1235-					current->type = StyleType::COUNTER;
1236-				} else if (parts[0][0] == "@font-face") {
1237-					current->type = StyleType::FONTFACE;
1238-				} else if (parts[0][0] == "@font-feature") {
1239-					current->type = StyleType::FONTFEATURE;
1240-				} else if (parts[0][0] == "@swash") {
1241-					current->type = StyleType::SWASH;
1242-				} else if (parts[0][0] == "@annotation") {
1243-					current->type = StyleType::ANNOTATION;
1244-				} else if (parts[0][0] == "@ornaments") {
1245-					current->type = StyleType::ORNAMENTS;
1246-				} else if (parts[0][0] == "@stylistic") {
1247-					current->type = StyleType::STYLISTIC;
1248-				} else if (parts[0][0] == "@styleset") {
1249-					current->type = StyleType::STYLESET;
1250-				} else if (parts[0][0] == "@character-varient") {
1251-					current->type = StyleType::CHARACTERVARIANT;
1252-				} else if (parts[0][0] == "@font-palette-values") {
1253-					current->type = StyleType::FONTPALETTEVALUES;
1254-					current->name = parts[0][2];
1255-				} else if (parts[0][0] == "@keyframes") {
1256-					current->type = StyleType::KEYFRAMES;
1257-					current->name = parts[0][2];
1258-				} else if (parts[0][0] == "@layer") {
1259-					current->type = StyleType::LAYER;
1260-				} else if (parts[0][0] == "@media") {
1261-					current->type = StyleType::MEDIA;
1262-				} else if (parts[0][0] == "@property") {
1263-					current->type = StyleType::PROPERTY;
1264-					current->name = parts[0][2];
1265-				} else if (parts[0][0] == "@starting-style") {
1266-					current->type = StyleType::STARTINGSTYLE;
1267-				}
1268-			} else {
1269-				current->type = StyleType::STYLE;
1270-			}
1271-			
1272-
1273 			buffer.clear();
1274 		} else if (ch == '}') {
1275 				
1276@@ -1632,54 +1334,276 @@ Style parseCSS(std::istream& inputStream) {
1277 	return root;
1278 }
1279 
1280-std::unordered_map<KeyType, std::vector<Unit>> parseCSSInline(std::string inlineStyles) {
1281-	std::unordered_map<KeyType, std::vector<Unit>> props;
1282+struct ParsedDocument {
1283+	Node document;
1284+	Style CSS;
1285+};
1286 
1287-	std::string buffer;
1288+ParsedDocument parseDocument(std::istream& inputStream) {
1289+	Node root;
1290+	root.setTagName("root");
1291 
1292-	for (size_t i = 0; i < inlineStyles.length(); i++) {
1293-		char ch = inlineStyles[i];
1294+	Node* currentNode = &root;
1295 
1296-		if (ch == ';' || i >= inlineStyles.length()-1) {
1297-			// If this is a ';' then it will be removed in the last else if statement
1298-			buffer.push_back(ch);
1299-			std::string key;
1300-			std::string value;
1301-			bool keyFound = false;
1302-			for (size_t i = 0; i < buffer.length(); i++) {
1303-				char c = buffer[i];
1304-				if (!keyFound && c == ':') {
1305-					keyFound = true;
1306-				} else if (!std::isspace(c) && !keyFound) {
1307-					key += c;
1308-				} else if (keyFound &&
1309-						!(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1])) &&
1310-						c != ';'
1311-					  ) {
1312-					// if the key is found and the character isn't a ':'
1313-					// 	also (if c is a space and the next c will be a space)
1314-					// 	this is to auto trim duplicate spaces from the value
1315-					value += c;
1316-					
1317+	bool inTag = false;
1318+	bool escaped = false;
1319+	bool inQuote = false;
1320+	bool inComment = false;
1321+	bool waitToClose = false;
1322+	char quoteType = '"';
1323+
1324+	std::string token;
1325+
1326+	// CSS stuff
1327+	std::vector<Node*> cssElements;
1328+
1329+	char current;
1330+	while (inputStream.get(current)) {
1331+		// Finds the --> and removes it then resets inComment
1332+		if (inComment) {
1333+			// added the peek to prevent hitting on every -
1334+			if (current == '-' && inputStream.peek() == '-') {
1335+				char a,b;
1336+				// load the next two
1337+				if (inputStream.get(a) && inputStream.get(b)) {
1338+					// We know a == -
1339+					if (b == '>') {
1340+						// Close the comment
1341+						inComment = false;
1342+					}
1343+				}
1344+
1345+				if (!inComment) {
1346+					// we don't put anything back and that puts us where we need to be
1347+					continue;
1348+				} else {
1349+					// Not the end 
1350+					inputStream.putback(b);
1351+					inputStream.putback(a);
1352 				}
1353 			}
1354+			continue;
1355+		}
1356 
1357-			KeyType keyENUM;
1358+		std::string tagName = currentNode->getTagName();
1359+		if (!waitToClose && (
1360+					tagName == "style" || 
1361+					tagName == "script" ||
1362+					tagName == "textarea" 
1363+					) && current == '<' && inputStream.peek() == '/' ) {
1364+			// If its the starting of a closing tag and we are inside of a special tag
1365+			// we need to see if the upcoming closing tag is the correct closing tag
1366+			// if it is then we put the read ahead back and waitToClose. This will let normal parsing
1367+			// continue
1368+			const size_t readTo = tagName.length()+2;
1369+			std::string buffer(readTo, '\0');
1370 
1371-			if (auto it = keySearch.find(key); it != keySearch.end()) {
1372-				keyENUM = it->second;
1373+			inputStream.read(&buffer[0], readTo);
1374+
1375+			if (buffer == "/"+tagName+">") {
1376+				waitToClose = true;
1377 			} else {
1378-				std::cout << "Invalid property name: " << key << std::endl;
1379+				token += current;
1380 			}
1381-			props[keyENUM] = getUnit(value);
1382 
1383-			buffer.clear();
1384-		} else {
1385-			buffer.push_back(ch);
1386+			for (int i = readTo-1; i>= 0; i--) {
1387+				inputStream.putback(buffer[i]);
1388+			}
1389+			if (!waitToClose) {
1390+				continue;
1391+			}
1392+		} else if (!waitToClose && (
1393+					tagName == "style" || 
1394+					tagName == "script" ||
1395+					tagName == "textarea" 
1396+					)) {
1397+			token += current;
1398+			continue;
1399 		}
1400+
1401+		if (inTag) {
1402+			 if ((current == '"' || current == '\'') && !escaped) {
1403+				if (inQuote && current == quoteType) {
1404+					inQuote = false;
1405+				} else if (!inQuote) {
1406+					inQuote = true;
1407+					quoteType = current;
1408+				}
1409+			}
1410+
1411+			if (current == '>' && !inQuote && !escaped) {
1412+				inTag = false;
1413+				waitToClose = false;
1414+				// Even if the next tag is next still add a text tag as we can just check if its empty and remove it
1415+				// this check would still have to be done either way so we aren't wasting anything
1416+				if (token.length() > 0) {
1417+					bool empty = true;
1418+					bool closingTag = false;
1419+					for (size_t i = 0; i < token.length(); i++) {
1420+						auto c = token[i];
1421+						if (std::isspace(c)) {
1422+							continue;
1423+						} else if (c == '/') {
1424+							closingTag = true;
1425+						} else {
1426+							empty = false;
1427+							break;
1428+						}
1429+					}
1430+
1431+					bool selfClosing = false;
1432+					int selfClosingPosition = token.length()-1;
1433+					if (!closingTag) {
1434+						for (int i = token.length()-1; i >= 0; i--) {
1435+							auto c = token[i];
1436+							if (std::isspace(c)) {
1437+								continue;
1438+							} else if (c == '/') {
1439+								selfClosingPosition = i;
1440+								selfClosing = true;
1441+								break;
1442+							} else {
1443+								break;
1444+							}
1445+						}
1446+					}
1447+
1448+					if (!empty) {
1449+						if (selfClosing) {
1450+							//Create element and don't jump inside
1451+							// Use node instead of currentNode because we do not need to jump into the created element
1452+							auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
1453+							auto node = currentNode->createElement(attrs["tagName"]);
1454+							for (auto const& pair : attrs) {
1455+								// All attributes are stored as strings so we can just throw them in
1456+								node->setAttribute(pair.first, pair.second);
1457+							}
1458+							if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
1459+								cssElements.push_back(node);
1460+							}
1461+						} else if (closingTag) {
1462+							// Checksum and tree move
1463+							std::string tagName = "";
1464+							for (auto t : token) {
1465+								if (!std::isspace(t) && t != '/') {
1466+									tagName += t;
1467+								} else if (t == '/') {
1468+									// For closing tags we just want the name
1469+									continue;
1470+								} else if (tagName.length() > 0) {
1471+									break;
1472+								}
1473+							}
1474+							if (currentNode->getTagName() == tagName) {
1475+								currentNode = currentNode->parent;
1476+							} else {
1477+								std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
1478+							}
1479+						} else {
1480+							// Create a element and jump inside
1481+							auto attrs = parseAttributes(token);
1482+							// Don't add elements like <!DOCTYPE>
1483+							if (attrs["tagName"][0] != '!') {
1484+								currentNode = currentNode->createElement(attrs["tagName"]);
1485+								for (auto const& pair : attrs) {
1486+									// All attributes are stored as strings so we can just throw them in
1487+									currentNode->setAttribute(pair.first, pair.second);
1488+								}
1489+								if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
1490+									cssElements.push_back(currentNode);
1491+								}
1492+							}
1493+
1494+						}
1495+					}
1496+				}
1497+
1498+				token = "";	
1499+				continue;
1500+			}
1501+		} else if (!escaped && current == '<') {
1502+			// if the next character is a !
1503+			if (inputStream.peek() == '!') {
1504+				char a,b,c;
1505+				// load the next three characters (includes !)
1506+				if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
1507+					// We know a == !
1508+					if (b == '-' && c == '-') {
1509+						inComment = true;
1510+					}
1511+				}
1512+
1513+				if (inComment) {
1514+					continue;
1515+				} else {
1516+					// Not a comment add all back
1517+					inputStream.putback(c);
1518+					inputStream.putback(b);
1519+					inputStream.putback(a);
1520+				}
1521+			}
1522+			inTag = true;
1523+			
1524+			// Here's where you actually make the text node and above the real nodes
1525+			// can also prob make the vector of tokens a single variable
1526+			// it really just needs to be a data string bc we know the type based on inTag
1527+
1528+
1529+			bool hasText = false;
1530+			for (auto t : token) {
1531+				if (!std::isspace(t)) {
1532+					hasText = true;
1533+					break;
1534+				}
1535+			}
1536+
1537+			// TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
1538+			// | Will need something similar for innerHTML
1539+			if (hasText) {
1540+				auto node = currentNode->createElement("text");
1541+				node->setAttribute("innerText", token);
1542+			}
1543+
1544+
1545+			token = "";
1546+			continue;
1547+		}
1548+		
1549+		escaped = false;
1550+
1551+		if (current == '\\') { //'
1552+			escaped = true;
1553+			continue;
1554+		}
1555+
1556+		token += current;
1557 	}
1558 
1559-	return props;
1560-}
1561+	ParsedDocument doc;
1562+	doc.document = root;
1563+
1564+	Style style;
1565 
1566+	for (size_t i = 0; i < cssElements.size(); i++) {
1567+		Node* el = cssElements[i];
1568 
1569+		if (el->getTagName() == "link" && el->getAttribute("rel") == "stylesheet") {
1570+			std::ifstream file(el->getAttribute("href"));
1571+			if (!file.is_open()) {
1572+				std::cerr << "Failed to open file!" << std::endl;
1573+			} else {
1574+				Style s = parseCSS(file);
1575+				style.addChild(s);
1576+			}
1577+			file.close();
1578+		} else if (el->getTagName() == "style") {
1579+			std::istringstream iss(el->children[0].getAttribute("innerText"));
1580+			Style s = parseCSS(iss);
1581+			style.addChild(s);
1582+		}
1583+	}
1584+
1585+	doc.CSS = style;
1586+
1587+	return doc;
1588+}
1589diff --git a/tests/css_parser.cc b/tests/css_parser.cc
1590index c5a1524..2bb474b 100755
1591--- a/tests/css_parser.cc
1592+++ b/tests/css_parser.cc
1593@@ -281,36 +281,4 @@ html#nested {
1594 	}
1595 
1596 }
1597-/*
1598-TEST_CASE("parseCSSInline can parse inline styles") {
1599-	SECTION("parseCSSInline can parse a single property with a ending ;") {
1600-		std::string inlineStyles = "background: yellow;";
1601-		compareInlineStyles(inlineStyles, {
1602-				{"background", "yellow"}
1603-				});
1604-	}
1605 
1606-	SECTION("parseCSSInline can parse a single property without a ending ;") {
1607-		std::string inlineStyles = "background: red";
1608-		compareInlineStyles(inlineStyles, {
1609-				{"background", "red"}
1610-				});
1611-	}
1612-	SECTION("parseCSSInline can parse multiple properties with a ending ;") {
1613-		std::string inlineStyles = "background: yellow; color: red; font-size: 10px;";
1614-		compareInlineStyles(inlineStyles, {
1615-				{"background", "yellow"},
1616-				{"color", "red"},
1617-				{"font-size", "10px"}
1618-				});
1619-	}
1620-
1621-	SECTION("parseCSSInline can parse multiple properties without a ending ;") {
1622-		std::string inlineStyles = "background: red; color: yellow; font-size: 10px";
1623-		compareInlineStyles(inlineStyles, {
1624-				{"background", "red"},
1625-				{"color", "yellow"},
1626-				{"font-size", "10px"},
1627-				});
1628-	}
1629-}*/