diff --git a/assets/kenny/arrowBeige_left.png b/assets/kenny/arrowBeige_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d2fdaae0a322aa0b01767f6838018fbe99f031b
Binary files /dev/null and b/assets/kenny/arrowBeige_left.png differ
diff --git a/assets/kenny/arrowBeige_right.png b/assets/kenny/arrowBeige_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..166bbfe83c11f9f8918309bd790fa0b8b1923ecd
Binary files /dev/null and b/assets/kenny/arrowBeige_right.png differ
diff --git a/assets/kenny/arrowBlue_left.png b/assets/kenny/arrowBlue_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..05d3b3b7bd788db6abf3351e1eba315c0f6f152b
Binary files /dev/null and b/assets/kenny/arrowBlue_left.png differ
diff --git a/assets/kenny/arrowBlue_right.png b/assets/kenny/arrowBlue_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..51fb299c4537f2a8b5a16ae0089ab0e41e0eecec
Binary files /dev/null and b/assets/kenny/arrowBlue_right.png differ
diff --git a/assets/kenny/arrowBrown_left.png b/assets/kenny/arrowBrown_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..de9961bc391b207b163a09827fd288127b163d14
Binary files /dev/null and b/assets/kenny/arrowBrown_left.png differ
diff --git a/assets/kenny/arrowBrown_right.png b/assets/kenny/arrowBrown_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a6b571d6f308991f5822300c6f8ec770bbdba1e
Binary files /dev/null and b/assets/kenny/arrowBrown_right.png differ
diff --git a/assets/kenny/arrowSilver_left.png b/assets/kenny/arrowSilver_left.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ed60b705238c5782b255868c5198233edbda554
Binary files /dev/null and b/assets/kenny/arrowSilver_left.png differ
diff --git a/assets/kenny/arrowSilver_right.png b/assets/kenny/arrowSilver_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..be71d37da86af30a30c960197227656aca4aee1b
Binary files /dev/null and b/assets/kenny/arrowSilver_right.png differ
diff --git a/assets/kenny/barBack_horizontalLeft.png b/assets/kenny/barBack_horizontalLeft.png
new file mode 100644
index 0000000000000000000000000000000000000000..801e6b5b29ba00f5009a9fcd462cfb4dabd77931
Binary files /dev/null and b/assets/kenny/barBack_horizontalLeft.png differ
diff --git a/assets/kenny/barBack_horizontalMid.png b/assets/kenny/barBack_horizontalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..319bdbc38ebf193cee5310fdbb35ab589a5a3475
Binary files /dev/null and b/assets/kenny/barBack_horizontalMid.png differ
diff --git a/assets/kenny/barBack_horizontalRight.png b/assets/kenny/barBack_horizontalRight.png
new file mode 100644
index 0000000000000000000000000000000000000000..c9a850f506334abd9d52f64f744c7387cab7a279
Binary files /dev/null and b/assets/kenny/barBack_horizontalRight.png differ
diff --git a/assets/kenny/barBack_verticalBottom.png b/assets/kenny/barBack_verticalBottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..f0e7064bd2886df713fc7e548b237c3021e6e259
Binary files /dev/null and b/assets/kenny/barBack_verticalBottom.png differ
diff --git a/assets/kenny/barBack_verticalMid.png b/assets/kenny/barBack_verticalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d3e8fd2b5e605adb97a592da9c0d528c352c1f9
Binary files /dev/null and b/assets/kenny/barBack_verticalMid.png differ
diff --git a/assets/kenny/barBack_verticalTop.png b/assets/kenny/barBack_verticalTop.png
new file mode 100644
index 0000000000000000000000000000000000000000..543cd193f4438d0971841dc245e78dda4b4de964
Binary files /dev/null and b/assets/kenny/barBack_verticalTop.png differ
diff --git a/assets/kenny/barBlue_horizontalBlue.png b/assets/kenny/barBlue_horizontalBlue.png
new file mode 100644
index 0000000000000000000000000000000000000000..5367dba38591073c177329b2d08825f0decf9eaf
Binary files /dev/null and b/assets/kenny/barBlue_horizontalBlue.png differ
diff --git a/assets/kenny/barBlue_horizontalLeft.png b/assets/kenny/barBlue_horizontalLeft.png
new file mode 100644
index 0000000000000000000000000000000000000000..0245eb3b533adb39089d3b5d5544f3040ce18938
Binary files /dev/null and b/assets/kenny/barBlue_horizontalLeft.png differ
diff --git a/assets/kenny/barBlue_horizontalRight.png b/assets/kenny/barBlue_horizontalRight.png
new file mode 100644
index 0000000000000000000000000000000000000000..e94c0c61e76ad123581c93972a91659a6ce7cec8
Binary files /dev/null and b/assets/kenny/barBlue_horizontalRight.png differ
diff --git a/assets/kenny/barBlue_verticalBottom.png b/assets/kenny/barBlue_verticalBottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..2834db913084b0aa6402b3e2b8db40d1b54771e8
Binary files /dev/null and b/assets/kenny/barBlue_verticalBottom.png differ
diff --git a/assets/kenny/barBlue_verticalMid.png b/assets/kenny/barBlue_verticalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..7629e5527e7a9d9e2d430011c4ab7b143122e5d3
Binary files /dev/null and b/assets/kenny/barBlue_verticalMid.png differ
diff --git a/assets/kenny/barBlue_verticalTop.png b/assets/kenny/barBlue_verticalTop.png
new file mode 100644
index 0000000000000000000000000000000000000000..18dda588ea2c163cac750aae5d5b832ed7e33cc5
Binary files /dev/null and b/assets/kenny/barBlue_verticalTop.png differ
diff --git a/assets/kenny/barGreen_horizontalLeft.png b/assets/kenny/barGreen_horizontalLeft.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b08f21c6657ad089f2371a30c866dd0f7d2ca87
Binary files /dev/null and b/assets/kenny/barGreen_horizontalLeft.png differ
diff --git a/assets/kenny/barGreen_horizontalMid.png b/assets/kenny/barGreen_horizontalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..f7b5d775bebc60dc63879a9f100fa2dc4056366b
Binary files /dev/null and b/assets/kenny/barGreen_horizontalMid.png differ
diff --git a/assets/kenny/barGreen_horizontalRight.png b/assets/kenny/barGreen_horizontalRight.png
new file mode 100644
index 0000000000000000000000000000000000000000..407b61b535d91dcbbd524f03d9f52bfd556bf039
Binary files /dev/null and b/assets/kenny/barGreen_horizontalRight.png differ
diff --git a/assets/kenny/barGreen_verticalBottom.png b/assets/kenny/barGreen_verticalBottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f733d4c07fa0bfad2d060ece7260ac0c573874c
Binary files /dev/null and b/assets/kenny/barGreen_verticalBottom.png differ
diff --git a/assets/kenny/barGreen_verticalMid.png b/assets/kenny/barGreen_verticalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..03ff210f290097aadfdadb8d958f16de269f4feb
Binary files /dev/null and b/assets/kenny/barGreen_verticalMid.png differ
diff --git a/assets/kenny/barGreen_verticalTop.png b/assets/kenny/barGreen_verticalTop.png
new file mode 100644
index 0000000000000000000000000000000000000000..235afa0b4f1be3e64c4a4b26ed7471eb58e6d8cb
Binary files /dev/null and b/assets/kenny/barGreen_verticalTop.png differ
diff --git a/assets/kenny/barRed_horizontalLeft.png b/assets/kenny/barRed_horizontalLeft.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d8e7a6f84084924da9526600d46c5c8107a3313
Binary files /dev/null and b/assets/kenny/barRed_horizontalLeft.png differ
diff --git a/assets/kenny/barRed_horizontalMid.png b/assets/kenny/barRed_horizontalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..99da8810a636da75e56777141a476a3349270a2c
Binary files /dev/null and b/assets/kenny/barRed_horizontalMid.png differ
diff --git a/assets/kenny/barRed_horizontalRight.png b/assets/kenny/barRed_horizontalRight.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ec2bd4fd1b9336a1ad147e72e0b90cd8a506335
Binary files /dev/null and b/assets/kenny/barRed_horizontalRight.png differ
diff --git a/assets/kenny/barRed_verticalBottom.png b/assets/kenny/barRed_verticalBottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..767d6a4c607e94fc625ef175be65ec02caefa3cf
Binary files /dev/null and b/assets/kenny/barRed_verticalBottom.png differ
diff --git a/assets/kenny/barRed_verticalMid.png b/assets/kenny/barRed_verticalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..934e6f80d48c32fd19067d255ded269a8d157e8c
Binary files /dev/null and b/assets/kenny/barRed_verticalMid.png differ
diff --git a/assets/kenny/barRed_verticalTop.png b/assets/kenny/barRed_verticalTop.png
new file mode 100644
index 0000000000000000000000000000000000000000..35101937914a1de0c7b84c3fe246811b47fa7391
Binary files /dev/null and b/assets/kenny/barRed_verticalTop.png differ
diff --git a/assets/kenny/barYellow_horizontalLeft.png b/assets/kenny/barYellow_horizontalLeft.png
new file mode 100644
index 0000000000000000000000000000000000000000..79c4b63fdde994900ebceb2c3eed8f52b9869908
Binary files /dev/null and b/assets/kenny/barYellow_horizontalLeft.png differ
diff --git a/assets/kenny/barYellow_horizontalMid.png b/assets/kenny/barYellow_horizontalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..38f37b6c06e4a31b51fdea38752be70e9a77f953
Binary files /dev/null and b/assets/kenny/barYellow_horizontalMid.png differ
diff --git a/assets/kenny/barYellow_horizontalRight.png b/assets/kenny/barYellow_horizontalRight.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d00f789fbbb163d20c5184a9970a02e6b218928
Binary files /dev/null and b/assets/kenny/barYellow_horizontalRight.png differ
diff --git a/assets/kenny/barYellow_verticalBottom.png b/assets/kenny/barYellow_verticalBottom.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0cc9d72dcdbe6c404639f66db905b3203fa29c0
Binary files /dev/null and b/assets/kenny/barYellow_verticalBottom.png differ
diff --git a/assets/kenny/barYellow_verticalMid.png b/assets/kenny/barYellow_verticalMid.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2491258bddf717d2026d2f804bd89a935b4e4a4
Binary files /dev/null and b/assets/kenny/barYellow_verticalMid.png differ
diff --git a/assets/kenny/barYellow_verticalTop.png b/assets/kenny/barYellow_verticalTop.png
new file mode 100644
index 0000000000000000000000000000000000000000..18b96ab2111d5f88218092ca0d6e816552326977
Binary files /dev/null and b/assets/kenny/barYellow_verticalTop.png differ
diff --git a/assets/kenny/buttonLong_beige.png b/assets/kenny/buttonLong_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..c9b35372b38e503f3e22f3a05c529cd04d82991c
Binary files /dev/null and b/assets/kenny/buttonLong_beige.png differ
diff --git a/assets/kenny/buttonLong_beige_pressed.png b/assets/kenny/buttonLong_beige_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..25378784c18ce313bfe6ec505cef5d962f8d3778
Binary files /dev/null and b/assets/kenny/buttonLong_beige_pressed.png differ
diff --git a/assets/kenny/buttonLong_blue.png b/assets/kenny/buttonLong_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..cbaf8d7817a9bb52324e9bdf9df811638a6a5bd8
Binary files /dev/null and b/assets/kenny/buttonLong_blue.png differ
diff --git a/assets/kenny/buttonLong_blue_pressed.png b/assets/kenny/buttonLong_blue_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ad0b5c2c60076b7a300246003fb4a8fbd35500c
Binary files /dev/null and b/assets/kenny/buttonLong_blue_pressed.png differ
diff --git a/assets/kenny/buttonLong_brown.png b/assets/kenny/buttonLong_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..299dc59d80e91af63053fdc0fe26d6224e43fcfe
Binary files /dev/null and b/assets/kenny/buttonLong_brown.png differ
diff --git a/assets/kenny/buttonLong_brown_pressed.png b/assets/kenny/buttonLong_brown_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..ec0a49445c98f2f9dbc6285973a913eca9ed7be2
Binary files /dev/null and b/assets/kenny/buttonLong_brown_pressed.png differ
diff --git a/assets/kenny/buttonLong_grey.png b/assets/kenny/buttonLong_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..b8c3fe09dc4da9950b0d82d926402377ecee47d8
Binary files /dev/null and b/assets/kenny/buttonLong_grey.png differ
diff --git a/assets/kenny/buttonLong_grey_pressed.png b/assets/kenny/buttonLong_grey_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..ef427446823017796b305b808a73e77fc5b9c149
Binary files /dev/null and b/assets/kenny/buttonLong_grey_pressed.png differ
diff --git a/assets/kenny/buttonRound_beige.png b/assets/kenny/buttonRound_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..69cbb7ba27900d43ff74555b266d232c88f5c35c
Binary files /dev/null and b/assets/kenny/buttonRound_beige.png differ
diff --git a/assets/kenny/buttonRound_blue.png b/assets/kenny/buttonRound_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6fb596e506aaf4d41c5eeec08c5ac60cbad38e0
Binary files /dev/null and b/assets/kenny/buttonRound_blue.png differ
diff --git a/assets/kenny/buttonRound_brown.png b/assets/kenny/buttonRound_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb824ea08112cade237ce70b0e480784d11b9f04
Binary files /dev/null and b/assets/kenny/buttonRound_brown.png differ
diff --git a/assets/kenny/buttonRound_grey.png b/assets/kenny/buttonRound_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..1fe89f8acde8482d72d2a95572dbf13759eea766
Binary files /dev/null and b/assets/kenny/buttonRound_grey.png differ
diff --git a/assets/kenny/buttonSquare_beige.png b/assets/kenny/buttonSquare_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..15ecc1cab174e47effe6b26d35594886c7b499e8
Binary files /dev/null and b/assets/kenny/buttonSquare_beige.png differ
diff --git a/assets/kenny/buttonSquare_beige_pressed.png b/assets/kenny/buttonSquare_beige_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..2465c870a33cdc902374d3a2136f40c18ce2dcbd
Binary files /dev/null and b/assets/kenny/buttonSquare_beige_pressed.png differ
diff --git a/assets/kenny/buttonSquare_blue.png b/assets/kenny/buttonSquare_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..c165e51f7feb1169ed3cea51e4f87617a1e6b93c
Binary files /dev/null and b/assets/kenny/buttonSquare_blue.png differ
diff --git a/assets/kenny/buttonSquare_blue_pressed.png b/assets/kenny/buttonSquare_blue_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ab31c5c340034da8b1d95e05f9563cb20774ba3
Binary files /dev/null and b/assets/kenny/buttonSquare_blue_pressed.png differ
diff --git a/assets/kenny/buttonSquare_brown.png b/assets/kenny/buttonSquare_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..1707aab0be8282fa1d7e1ed9e5f69f0170c21215
Binary files /dev/null and b/assets/kenny/buttonSquare_brown.png differ
diff --git a/assets/kenny/buttonSquare_brown_pressed.png b/assets/kenny/buttonSquare_brown_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..0ea2427e98d65f0db66cd11011356c813c226945
Binary files /dev/null and b/assets/kenny/buttonSquare_brown_pressed.png differ
diff --git a/assets/kenny/buttonSquare_grey.png b/assets/kenny/buttonSquare_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..f21b17e1b4b38c0bff17c3db279ec4b6a433ea8f
Binary files /dev/null and b/assets/kenny/buttonSquare_grey.png differ
diff --git a/assets/kenny/buttonSquare_grey_pressed.png b/assets/kenny/buttonSquare_grey_pressed.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf7b41072881803606334a14dae6e24b14145ae7
Binary files /dev/null and b/assets/kenny/buttonSquare_grey_pressed.png differ
diff --git a/assets/kenny/cursorGauntlet_blue.png b/assets/kenny/cursorGauntlet_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..ab9eaafe0d11b360e536799037004edab6b4dfbc
Binary files /dev/null and b/assets/kenny/cursorGauntlet_blue.png differ
diff --git a/assets/kenny/cursorGauntlet_bronze.png b/assets/kenny/cursorGauntlet_bronze.png
new file mode 100644
index 0000000000000000000000000000000000000000..379b6f5c889eb8de8b7ad6738b7fd92144d26b33
Binary files /dev/null and b/assets/kenny/cursorGauntlet_bronze.png differ
diff --git a/assets/kenny/cursorGauntlet_grey.png b/assets/kenny/cursorGauntlet_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..b6f84c34ad917931ce414044910c0f6693f9ae1a
Binary files /dev/null and b/assets/kenny/cursorGauntlet_grey.png differ
diff --git a/assets/kenny/cursorHand_beige.png b/assets/kenny/cursorHand_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff2bba7414d26868c7e3b9a1cf8e399ae0b653fb
Binary files /dev/null and b/assets/kenny/cursorHand_beige.png differ
diff --git a/assets/kenny/cursorHand_blue.png b/assets/kenny/cursorHand_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ee5baaf38d45a98f51b209a968502e46681c942
Binary files /dev/null and b/assets/kenny/cursorHand_blue.png differ
diff --git a/assets/kenny/cursorHand_grey.png b/assets/kenny/cursorHand_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..fa83d37ef5c20e9bda94ed989ff8383a4139c8b8
Binary files /dev/null and b/assets/kenny/cursorHand_grey.png differ
diff --git a/assets/kenny/cursorSword_bronze.png b/assets/kenny/cursorSword_bronze.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b43075a33e0f6bd05e39e834bc6ff5894bf76b8
Binary files /dev/null and b/assets/kenny/cursorSword_bronze.png differ
diff --git a/assets/kenny/cursorSword_gold.png b/assets/kenny/cursorSword_gold.png
new file mode 100644
index 0000000000000000000000000000000000000000..ccca5fd30a5ee35e6c5d59212e787285510d5033
Binary files /dev/null and b/assets/kenny/cursorSword_gold.png differ
diff --git a/assets/kenny/cursorSword_silver.png b/assets/kenny/cursorSword_silver.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b37fe8e588fd429e789e50b9fd8b5675f48bbad
Binary files /dev/null and b/assets/kenny/cursorSword_silver.png differ
diff --git a/assets/kenny/iconCheck_beige.png b/assets/kenny/iconCheck_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc4790e7f1e91579f31fc16ca705dacd2719ec21
Binary files /dev/null and b/assets/kenny/iconCheck_beige.png differ
diff --git a/assets/kenny/iconCheck_blue.png b/assets/kenny/iconCheck_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ef72cde22fb2f178e6c6d14853473bd40039dcc
Binary files /dev/null and b/assets/kenny/iconCheck_blue.png differ
diff --git a/assets/kenny/iconCheck_bronze.png b/assets/kenny/iconCheck_bronze.png
new file mode 100644
index 0000000000000000000000000000000000000000..cdaa2f239d8d53bcfdb88e300cde5de81ade3f9f
Binary files /dev/null and b/assets/kenny/iconCheck_bronze.png differ
diff --git a/assets/kenny/iconCheck_grey.png b/assets/kenny/iconCheck_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..e4016cf7d97d78ab3443a8b0df0f749b4311580e
Binary files /dev/null and b/assets/kenny/iconCheck_grey.png differ
diff --git a/assets/kenny/iconCircle_beige.png b/assets/kenny/iconCircle_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5e3ca1ed5671c2cfe54b60cc75e16902b6d5cd4
Binary files /dev/null and b/assets/kenny/iconCircle_beige.png differ
diff --git a/assets/kenny/iconCircle_blue.png b/assets/kenny/iconCircle_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..7062e5c7d1efbb13987f28a76400c89fa8676546
Binary files /dev/null and b/assets/kenny/iconCircle_blue.png differ
diff --git a/assets/kenny/iconCircle_brown.png b/assets/kenny/iconCircle_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..7abf40fe3ce29d58c2492c095da6c454a7b37e12
Binary files /dev/null and b/assets/kenny/iconCircle_brown.png differ
diff --git a/assets/kenny/iconCircle_grey.png b/assets/kenny/iconCircle_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..d35d80be281cb016b4f8eeeeb381969fb7b0d67a
Binary files /dev/null and b/assets/kenny/iconCircle_grey.png differ
diff --git a/assets/kenny/iconCross_beige.png b/assets/kenny/iconCross_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..32278dccd955874b321a7410a35a84342a32065a
Binary files /dev/null and b/assets/kenny/iconCross_beige.png differ
diff --git a/assets/kenny/iconCross_blue.png b/assets/kenny/iconCross_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ab70a18ce0590995d90340d15327f287575b306
Binary files /dev/null and b/assets/kenny/iconCross_blue.png differ
diff --git a/assets/kenny/iconCross_brown.png b/assets/kenny/iconCross_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..7cce69ada1e4cb5a85cfbf0b4acd6ab9b6f1a5cd
Binary files /dev/null and b/assets/kenny/iconCross_brown.png differ
diff --git a/assets/kenny/iconCross_grey.png b/assets/kenny/iconCross_grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..f57ccd8c322d5474a09f734c61f72fc63f65f442
Binary files /dev/null and b/assets/kenny/iconCross_grey.png differ
diff --git a/assets/kenny/panelInset_beige.png b/assets/kenny/panelInset_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..f92416fa221ee96a706413b02140b7ecf08e46e1
Binary files /dev/null and b/assets/kenny/panelInset_beige.png differ
diff --git a/assets/kenny/panelInset_beigeLight.png b/assets/kenny/panelInset_beigeLight.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ed1a7bc90f65bce6867a26062079b362c4a3b6d
Binary files /dev/null and b/assets/kenny/panelInset_beigeLight.png differ
diff --git a/assets/kenny/panelInset_blue.png b/assets/kenny/panelInset_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c2b5851e38c010d067501f099cbf88ebc97a492
Binary files /dev/null and b/assets/kenny/panelInset_blue.png differ
diff --git a/assets/kenny/panelInset_brown.png b/assets/kenny/panelInset_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc293f144b4acc58ac8c44ea2f7cb4ff532fc252
Binary files /dev/null and b/assets/kenny/panelInset_brown.png differ
diff --git a/assets/kenny/panel_beige.png b/assets/kenny/panel_beige.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4a1d99d5249af3b6cb7e68681bb806e3b95d24b
Binary files /dev/null and b/assets/kenny/panel_beige.png differ
diff --git a/assets/kenny/panel_beigeLight.png b/assets/kenny/panel_beigeLight.png
new file mode 100644
index 0000000000000000000000000000000000000000..8ccf82cf70d500ed7d5e627f70b54e56cb02ed2a
Binary files /dev/null and b/assets/kenny/panel_beigeLight.png differ
diff --git a/assets/kenny/panel_blue.png b/assets/kenny/panel_blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf58c662e703e08e83f31c56834224e6810ee1ac
Binary files /dev/null and b/assets/kenny/panel_blue.png differ
diff --git a/assets/kenny/panel_brown.png b/assets/kenny/panel_brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..97c381ba6000d74d29284c3e8bef0f9929c8ffdb
Binary files /dev/null and b/assets/kenny/panel_brown.png differ
diff --git a/assets/panel.png b/assets/panel.png
new file mode 100644
index 0000000000000000000000000000000000000000..739934c54b3e2103ddc44c3283f03c9dc08ac4a5
Binary files /dev/null and b/assets/panel.png differ
diff --git a/bevy_kayak_ui/src/bevy_context.rs b/bevy_kayak_ui/src/bevy_context.rs
index 0c2c974e74e5116e913d90fda7e2ad7c65e681c2..5eb9cfe732618bfef4450c4c14a44fb4a3a7e7a8 100644
--- a/bevy_kayak_ui/src/bevy_context.rs
+++ b/bevy_kayak_ui/src/bevy_context.rs
@@ -23,9 +23,6 @@ impl BevyContext {
 
         if let Ok(mut kayak_context) = kayak_context.write() {
             f(&mut app_styles, &mut kayak_context);
-
-            kayak_context.render();
-
             kayak_context.widget_manager.dirty(true);
         }
 
diff --git a/bevy_kayak_ui/src/lib.rs b/bevy_kayak_ui/src/lib.rs
index fee1bbc002e4c64ee1a418d83d638763af7f2603..1f1d45ff7c38e3add19967a1d7e743095733ed21 100644
--- a/bevy_kayak_ui/src/lib.rs
+++ b/bevy_kayak_ui/src/lib.rs
@@ -1,7 +1,7 @@
 use bevy::{
     input::{mouse::MouseButtonInput, ElementState},
     math::Vec2,
-    prelude::{EventReader, MouseButton, Plugin, Res, ResMut},
+    prelude::{EventReader, IntoExclusiveSystem, MouseButton, Plugin, Res, World},
     render2::color::Color,
     window::{CursorMoved, Windows},
 };
@@ -13,6 +13,7 @@ mod render;
 pub use bevy_context::BevyContext;
 pub use camera::*;
 use kayak_core::InputEvent;
+pub use render::unified::image::ImageManager;
 
 #[derive(Default)]
 pub struct BevyKayakUIPlugin;
@@ -21,7 +22,8 @@ impl Plugin for BevyKayakUIPlugin {
     fn build(&self, app: &mut bevy::prelude::App) {
         app.add_plugin(render::BevyKayakUIRenderPlugin)
             .add_plugin(camera::KayakUICameraPlugin)
-            .add_system(update);
+            .add_system(process_events)
+            .add_system(update.exclusive_system());
     }
 }
 
@@ -29,11 +31,22 @@ pub(crate) fn to_bevy_color(color: &kayak_core::color::Color) -> Color {
     Color::rgba(color.r, color.g, color.b, color.a)
 }
 
-pub fn update(
-    bevy_context: ResMut<BevyContext>,
+pub fn update(world: &mut World) {
+    let bevy_context = world.remove_resource::<BevyContext>().unwrap();
+    if let Ok(mut context) = bevy_context.kayak_context.write() {
+        context.set_global_state(std::mem::take(world));
+        context.render();
+        *world = context.take_global_state::<World>().unwrap()
+    }
+
+    world.insert_resource(bevy_context);
+}
+
+pub fn process_events(
+    bevy_context: Res<BevyContext>,
+    windows: Res<Windows>,
     mut cursor_moved_events: EventReader<CursorMoved>,
     mut mouse_button_input_events: EventReader<MouseButtonInput>,
-    windows: Res<Windows>,
 ) {
     let window_size = if let Some(window) = windows.get_primary() {
         Vec2::new(window.width(), window.height())
@@ -42,9 +55,8 @@ pub fn update(
     };
 
     if let Ok(mut context) = bevy_context.kayak_context.write() {
-        context.render();
-
         let mut input_events = Vec::new();
+
         for event in cursor_moved_events.iter() {
             input_events.push(InputEvent::MouseMoved((
                 event.position.x as f32,
diff --git a/bevy_kayak_ui/src/render/mod.rs b/bevy_kayak_ui/src/render/mod.rs
index 6b41bf0457c2946305253858b7e5199855b0cba4..970ea9c859b141e90548d502c16562a44cb9a9d0 100644
--- a/bevy_kayak_ui/src/render/mod.rs
+++ b/bevy_kayak_ui/src/render/mod.rs
@@ -19,7 +19,7 @@ use self::ui_pass::TransparentUI;
 
 mod ui_pass;
 mod ui_pass_driver;
-mod unified;
+pub mod unified;
 
 pub mod node {
     pub const UI_PASS_DEPENDENCIES: &str = "ui_pass_dependencies";
diff --git a/bevy_kayak_ui/src/render/unified/font/extract.rs b/bevy_kayak_ui/src/render/unified/font/extract.rs
index ce8844dde4d434a346ecd64e354e29836e7a6317..43bce7d403757fe05d37f183525f19f4003290b2 100644
--- a/bevy_kayak_ui/src/render/unified/font/extract.rs
+++ b/bevy_kayak_ui/src/render/unified/font/extract.rs
@@ -137,6 +137,9 @@ pub fn extract_texts(
                         quad_type: UIQuadType::Text,
                         type_index: 0,
                         border_radius: (0.0, 0.0, 0.0, 0.0),
+                        image: None,
+                        uv_max: None,
+                        uv_min: None,
                     },
                 });
 
diff --git a/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs b/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs
index f45898b206eac724845a3ff152defc20f0c621b3..f9d526c3a8bacd28907f885bcaff608bb1831ae6 100644
--- a/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs
+++ b/bevy_kayak_ui/src/render/unified/font/font_texture_cache.rs
@@ -111,7 +111,7 @@ impl FontTextureCache {
                                 resource: BindingResource::Sampler(&gpu_image.sampler),
                             },
                         ],
-                        layout: &pipeline.image_layout,
+                        layout: &pipeline.font_image_layout,
                     });
 
                     self.bind_groups
@@ -305,7 +305,6 @@ impl FontTextureCache {
         atlas_texture: &GpuImage,
         size: Vec2,
     ) {
-        dbg!(size);
         Self::create_texture(
             images,
             font_handle.clone_weak(),
@@ -333,7 +332,7 @@ impl FontTextureCache {
                     resource: BindingResource::Sampler(&gpu_image.sampler),
                 },
             ],
-            layout: &pipeline.image_layout,
+            layout: &pipeline.font_image_layout,
         });
 
         bind_groups.insert(font_handle.clone_weak(), binding);
diff --git a/bevy_kayak_ui/src/render/unified/font/mod.rs b/bevy_kayak_ui/src/render/unified/font/mod.rs
index 1ae275c690267860df26b5dc60f641c700a4a052..8b2a9fff050a30a686c6dd3d4c92c376f2159046 100644
--- a/bevy_kayak_ui/src/render/unified/font/mod.rs
+++ b/bevy_kayak_ui/src/render/unified/font/mod.rs
@@ -90,14 +90,11 @@ pub fn set_font_texture(
 ) {
     // quick and dirty, run this for all textures anytime a texture is created.
     for event in texture_events.iter() {
-        dbg!(&event);
         match event {
             AssetEvent::Created { handle } => {
                 let handle_path = asset_server.get_handle_path(handle).unwrap();
-                dbg!(&handle_path);
                 if handle_path.path().to_str().unwrap().contains("roboto") {
                     if let Some(mut texture) = textures.get_mut(handle) {
-                        dbg!("Setting font texture!");
                         texture.texture_descriptor.format = TextureFormat::Rgba8Unorm;
                         texture.sampler_descriptor.min_filter = FilterMode::Linear;
                         texture.sampler_descriptor.mipmap_filter = FilterMode::Linear;
diff --git a/bevy_kayak_ui/src/render/unified/image/extract.rs b/bevy_kayak_ui/src/render/unified/image/extract.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dfee468af1f4e1c74d456fcae9c05923d5b9f68f
--- /dev/null
+++ b/bevy_kayak_ui/src/render/unified/image/extract.rs
@@ -0,0 +1,60 @@
+use bevy::{
+    math::Vec2,
+    prelude::{Commands, Res},
+    render2::color::Color,
+    sprite2::Rect,
+};
+use kayak_core::render_primitive::RenderPrimitive;
+
+use crate::{
+    render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
+    BevyContext, ImageManager,
+};
+
+pub fn extract_images(
+    mut commands: Commands,
+    context: Res<BevyContext>,
+    image_manager: Res<ImageManager>,
+) {
+    let render_commands = if let Ok(context) = context.kayak_context.read() {
+        context.widget_manager.build_render_primitives()
+    } else {
+        vec![]
+    };
+
+    let image_commands: Vec<&RenderPrimitive> = render_commands
+        .iter()
+        .filter(|command| matches!(command, RenderPrimitive::Image { .. }))
+        .collect::<Vec<_>>();
+
+    let mut extracted_quads = Vec::new();
+    for render_primitive in image_commands {
+        let (layout, handle) = match render_primitive {
+            RenderPrimitive::Image { layout, handle } => (layout, handle),
+            _ => panic!(""),
+        };
+
+        extracted_quads.push(ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(layout.posx, layout.posy),
+                    max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height),
+                },
+                color: Color::WHITE,
+                vertex_index: 0,
+                char_id: 0,
+                z_index: layout.z_index,
+                font_handle: None,
+                quad_type: UIQuadType::Image,
+                type_index: 0,
+                border_radius: (0.0, 0.0, 0.0, 0.0),
+                image: image_manager
+                    .get_handle(handle)
+                    .and_then(|a| Some(a.clone_weak())),
+                uv_max: None,
+                uv_min: None,
+            },
+        });
+    }
+    commands.spawn_batch(extracted_quads);
+}
diff --git a/bevy_kayak_ui/src/render/unified/image/image_manager.rs b/bevy_kayak_ui/src/render/unified/image/image_manager.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0493f8dbc26fe3322fc17b0b36dc4e379383675f
--- /dev/null
+++ b/bevy_kayak_ui/src/render/unified/image/image_manager.rs
@@ -0,0 +1,34 @@
+use bevy::{prelude::Handle, render2::texture::Image, utils::HashMap};
+
+#[derive(Debug, Clone)]
+pub struct ImageManager {
+    count: u16,
+    mapping: HashMap<u16, Handle<Image>>,
+    reverse_mapping: HashMap<Handle<Image>, u16>,
+}
+
+impl ImageManager {
+    pub fn new() -> Self {
+        Self {
+            count: 0,
+            mapping: HashMap::default(),
+            reverse_mapping: HashMap::default(),
+        }
+    }
+
+    pub fn get(&mut self, image_handle: &Handle<Image>) -> u16 {
+        if let Some(id) = self.reverse_mapping.get(image_handle) {
+            return *id;
+        } else {
+            let id = self.count;
+            self.count += 1;
+            self.mapping.insert(id, image_handle.clone());
+            self.reverse_mapping.insert(image_handle.clone_weak(), id);
+            return id;
+        }
+    }
+
+    pub fn get_handle(&self, id: &u16) -> Option<&Handle<Image>> {
+        self.mapping.get(id)
+    }
+}
diff --git a/bevy_kayak_ui/src/render/unified/image/mod.rs b/bevy_kayak_ui/src/render/unified/image/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..549b0970c0884f0c0d46223f54a25a8a2ef87cbd
--- /dev/null
+++ b/bevy_kayak_ui/src/render/unified/image/mod.rs
@@ -0,0 +1,19 @@
+use bevy::{
+    prelude::Plugin,
+    render2::{RenderApp, RenderStage},
+};
+
+mod extract;
+mod image_manager;
+pub use image_manager::ImageManager;
+
+pub struct ImageRendererPlugin;
+
+impl Plugin for ImageRendererPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        app.insert_resource(ImageManager::new());
+
+        let render_app = app.sub_app(RenderApp);
+        render_app.add_system_to_stage(RenderStage::Extract, extract::extract_images);
+    }
+}
diff --git a/bevy_kayak_ui/src/render/unified/mod.rs b/bevy_kayak_ui/src/render/unified/mod.rs
index 37aeb58243aa976470811d76a83d949f8754ca34..152bdd7eb1a26c1f0fb6ac31e5fa56be8b51fdb9 100644
--- a/bevy_kayak_ui/src/render/unified/mod.rs
+++ b/bevy_kayak_ui/src/render/unified/mod.rs
@@ -9,7 +9,11 @@ use crate::render::{
     unified::pipeline::{DrawUI, QuadMeta, UnifiedPipeline},
 };
 
+use self::pipeline::ImageBindGroups;
+
 pub mod font;
+pub mod image;
+mod nine_patch;
 mod pipeline;
 mod quad;
 
@@ -26,6 +30,7 @@ impl Plugin for UnifiedRenderPlugin {
 
         let render_app = app.sub_app(RenderApp);
         render_app
+            .init_resource::<ImageBindGroups>()
             .init_resource::<UnifiedPipeline>()
             .init_resource::<QuadMeta>()
             .add_system_to_stage(RenderStage::Prepare, pipeline::prepare_quads)
@@ -41,6 +46,8 @@ impl Plugin for UnifiedRenderPlugin {
             .add(draw_quad);
 
         app.add_plugin(font::TextRendererPlugin)
-            .add_plugin(quad::QuadRendererPlugin);
+            .add_plugin(quad::QuadRendererPlugin)
+            .add_plugin(image::ImageRendererPlugin)
+            .add_plugin(nine_patch::NinePatchRendererPlugin);
     }
 }
diff --git a/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs b/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fdd47979cc4b97c650c0d5be41b93f8eda1f8d5d
--- /dev/null
+++ b/bevy_kayak_ui/src/render/unified/nine_patch/extract.rs
@@ -0,0 +1,260 @@
+use bevy::{
+    math::Vec2,
+    prelude::{Assets, Commands, Res},
+    render2::{color::Color, texture::Image},
+    sprite2::Rect,
+};
+use kayak_core::render_primitive::RenderPrimitive;
+
+use crate::{
+    render::unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType},
+    BevyContext, ImageManager,
+};
+
+pub fn extract_nine_patch(
+    mut commands: Commands,
+    context: Res<BevyContext>,
+    image_manager: Res<ImageManager>,
+    images: Res<Assets<Image>>,
+) {
+    let render_commands = if let Ok(context) = context.kayak_context.read() {
+        context.widget_manager.build_render_primitives()
+    } else {
+        vec![]
+    };
+
+    let image_commands: Vec<&RenderPrimitive> = render_commands
+        .iter()
+        .filter(|command| matches!(command, RenderPrimitive::NinePatch { .. }))
+        .collect::<Vec<_>>();
+
+    let mut extracted_quads = Vec::new();
+    for render_primitive in image_commands {
+        let (layout, handle, border) = match render_primitive {
+            RenderPrimitive::NinePatch {
+                layout,
+                handle,
+                border,
+            } => (layout, handle, border),
+            _ => panic!(""),
+        };
+
+        let image_handle = image_manager
+            .get_handle(handle)
+            .and_then(|a| Some(a.clone_weak()));
+
+        let image = images.get(image_handle.as_ref().unwrap());
+
+        if image.is_none() {
+            dbg!("Uh oh no image! :(");
+            continue;
+        }
+
+        let image_size = image
+            .and_then(|i| {
+                Some(Vec2::new(
+                    i.texture_descriptor.size.width as f32,
+                    i.texture_descriptor.size.height as f32,
+                ))
+            })
+            .unwrap();
+
+        let extracted_quad_template = ExtractedQuad {
+            rect: Rect {
+                min: Vec2::ZERO,
+                max: Vec2::ZERO,
+            },
+            color: Color::WHITE,
+            vertex_index: 0,
+            char_id: 0,
+            z_index: layout.z_index,
+            font_handle: None,
+            quad_type: UIQuadType::Image,
+            type_index: 0,
+            border_radius: (0.0, 0.0, 0.0, 0.0),
+            image: image_handle,
+            uv_max: None,
+            uv_min: None,
+        };
+
+        // TOP
+        let top_left_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(layout.posx, layout.posy),
+                    max: Vec2::new(layout.posx + border.left, layout.posy + border.top),
+                },
+                uv_min: Some(Vec2::new(0.0, border.top / image_size.y)),
+                uv_max: Some(Vec2::new(border.left / image_size.x, 0.0)),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(top_left_quad);
+
+        let top_right_pos_x = (layout.posx + layout.width) - border.left;
+        let top_right_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(top_right_pos_x, layout.posy),
+                    max: Vec2::new(top_right_pos_x + border.left, layout.posy + border.top),
+                },
+                uv_min: Some(Vec2::new(
+                    (image_size.x - border.left) / image_size.x,
+                    border.top / image_size.y,
+                )),
+                uv_max: Some(Vec2::new(1.0, 0.0)),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(top_right_quad);
+
+        let top_middle_pos_x = layout.posx + border.left;
+        let top_middle_size_x = layout.width - (border.left + border.right);
+        let top_middle_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(top_middle_pos_x, layout.posy),
+                    max: Vec2::new(
+                        top_middle_pos_x + top_middle_size_x,
+                        layout.posy + border.top,
+                    ),
+                },
+                uv_min: Some(Vec2::new(
+                    border.left / image_size.x,
+                    border.top / image_size.y,
+                )),
+                uv_max: Some(Vec2::new((image_size.x - border.left) / image_size.x, 0.0)),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(top_middle_quad);
+
+        // Bottom
+        let bottom_y_pos = layout.posy + (layout.height - border.bottom);
+        let bottom_left_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(layout.posx, bottom_y_pos),
+                    max: Vec2::new(layout.posx + border.left, bottom_y_pos + border.bottom),
+                },
+                uv_min: Some(Vec2::new(0.0, 1.0)),
+                uv_max: Some(Vec2::new(
+                    border.left / image_size.x,
+                    (image_size.y - border.bottom) / image_size.y,
+                )),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(bottom_left_quad);
+
+        let bottom_right_pos_x = (layout.posx + layout.width) - border.left;
+        let bottom_right_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(bottom_right_pos_x, bottom_y_pos),
+                    max: Vec2::new(bottom_right_pos_x + border.left, bottom_y_pos + border.top),
+                },
+                uv_min: Some(Vec2::new((image_size.x - border.left) / image_size.x, 1.0)),
+                uv_max: Some(Vec2::new(
+                    1.0,
+                    (image_size.y - border.bottom) / image_size.y,
+                )),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(bottom_right_quad);
+
+        let bottom_middle_pos_x = layout.posx + border.left;
+        let bottom_middle_size_x = layout.width - (border.left + border.right);
+        let bottom_middle_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(bottom_middle_pos_x, bottom_y_pos),
+                    max: Vec2::new(
+                        bottom_middle_pos_x + bottom_middle_size_x,
+                        bottom_y_pos + border.top,
+                    ),
+                },
+                uv_min: Some(Vec2::new(border.left / image_size.x, 1.0)),
+                uv_max: Some(Vec2::new(
+                    (image_size.x - border.left) / image_size.x,
+                    (image_size.y - border.bottom) / image_size.y,
+                )),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(bottom_middle_quad);
+
+        // Left + Right center
+        let left_middle_pos_y = layout.posy + border.top;
+        let left_middle_size_y = layout.height - (border.top + border.bottom);
+        let left_middle_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(layout.posx, left_middle_pos_y),
+                    max: Vec2::new(
+                        layout.posx + border.left,
+                        left_middle_pos_y + left_middle_size_y,
+                    ),
+                },
+                uv_min: Some(Vec2::new(
+                    0.0,
+                    (image_size.y - border.bottom) / image_size.y,
+                )),
+                uv_max: Some(Vec2::new(
+                    border.left / image_size.x,
+                    border.top / image_size.y,
+                )),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(left_middle_quad);
+
+        let right_middle_pos_x = layout.posx + (layout.width - border.right);
+        let right_middle_pos_y = layout.posy + border.top;
+        let right_middle_size_y = layout.height - (border.top + border.bottom);
+        let right_middle_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(right_middle_pos_x, right_middle_pos_y),
+                    max: Vec2::new(
+                        right_middle_pos_x + border.left,
+                        right_middle_pos_y + right_middle_size_y,
+                    ),
+                },
+                uv_min: Some(Vec2::new(
+                    (image_size.x - border.left) / image_size.x,
+                    (image_size.y - border.bottom) / image_size.y,
+                )),
+                uv_max: Some(Vec2::new(1.0, border.top / image_size.y)),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(right_middle_quad);
+
+        // Last quad in middle.
+        let middle_pos_x = layout.posx + border.left;
+        let middle_pos_y = layout.posy + border.top;
+        let middle_size_x = layout.width - (border.left + border.right);
+        let middle_size_y = layout.height - (border.top + border.bottom);
+        let middle_quad = ExtractQuadBundle {
+            extracted_quad: ExtractedQuad {
+                rect: Rect {
+                    min: Vec2::new(middle_pos_x, middle_pos_y),
+                    max: Vec2::new(middle_pos_x + middle_size_x, middle_pos_y + middle_size_y),
+                },
+                uv_min: Some(Vec2::new(
+                    border.left / image_size.x,
+                    border.top / image_size.y,
+                )),
+                uv_max: Some(Vec2::new(
+                    (image_size.x - border.right) / image_size.x,
+                    (image_size.y - border.bottom) / image_size.y,
+                )),
+                ..extracted_quad_template.clone()
+            },
+        };
+        extracted_quads.push(middle_quad);
+    }
+    commands.spawn_batch(extracted_quads);
+}
diff --git a/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs b/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..dfaec008a1ea982b0d6781f75d977a639ea2fc6b
--- /dev/null
+++ b/bevy_kayak_ui/src/render/unified/nine_patch/mod.rs
@@ -0,0 +1,15 @@
+use bevy::{
+    prelude::Plugin,
+    render2::{RenderApp, RenderStage},
+};
+
+mod extract;
+
+pub struct NinePatchRendererPlugin;
+
+impl Plugin for NinePatchRendererPlugin {
+    fn build(&self, app: &mut bevy::prelude::App) {
+        let render_app = app.sub_app(RenderApp);
+        render_app.add_system_to_stage(RenderStage::Extract, extract::extract_nine_patch);
+    }
+}
diff --git a/bevy_kayak_ui/src/render/unified/pipeline.rs b/bevy_kayak_ui/src/render/unified/pipeline.rs
index f110ca4fd630842b5def4579c9e89c7c55378df9..a787e7152ef7f32786639027c9b2d475acdfc320 100644
--- a/bevy_kayak_ui/src/render/unified/pipeline.rs
+++ b/bevy_kayak_ui/src/render/unified/pipeline.rs
@@ -4,26 +4,30 @@ use bevy::{
         lifetimeless::{Read, SQuery, SRes},
         SystemState,
     },
-    math::{const_vec3, Mat4, Quat, Vec3, Vec4},
+    math::{const_vec3, Mat4, Quat, Vec2, Vec3, Vec4},
     prelude::{Bundle, Component, Entity, FromWorld, Handle, Query, Res, ResMut, World},
     render2::{
         color::Color,
+        render_asset::RenderAssets,
         render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
         render_resource::{
             BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout,
-            BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendComponent,
-            BlendFactor, BlendOperation, BlendState, BufferBindingType, BufferSize, BufferUsages,
-            BufferVec, CachedPipelineId, ColorTargetState, ColorWrites, DynamicUniformVec,
-            FragmentState, FrontFace, MultisampleState, PolygonMode, PrimitiveState,
-            PrimitiveTopology, RenderPipelineCache, RenderPipelineDescriptor, Shader, ShaderStages,
-            TextureFormat, TextureSampleType, TextureViewDimension, VertexAttribute,
-            VertexBufferLayout, VertexFormat, VertexState, VertexStepMode,
+            BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType,
+            BlendComponent, BlendFactor, BlendOperation, BlendState, BufferBindingType, BufferSize,
+            BufferUsages, BufferVec, CachedPipelineId, ColorTargetState, ColorWrites,
+            DynamicUniformVec, Extent3d, FragmentState, FrontFace, MultisampleState, PolygonMode,
+            PrimitiveState, PrimitiveTopology, RenderPipelineCache, RenderPipelineDescriptor,
+            SamplerDescriptor, Shader, ShaderStages, TextureDescriptor, TextureDimension,
+            TextureFormat, TextureSampleType, TextureUsages, TextureViewDescriptor,
+            TextureViewDimension, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState,
+            VertexStepMode,
         },
         renderer::{RenderDevice, RenderQueue},
-        texture::{BevyDefault, GpuImage},
+        texture::{BevyDefault, GpuImage, Image},
         view::{ViewUniformOffset, ViewUniforms},
     },
     sprite2::Rect,
+    utils::HashMap,
 };
 use bytemuck::{Pod, Zeroable};
 use crevice::std140::AsStd140;
@@ -35,9 +39,11 @@ use crate::render::ui_pass::TransparentUI;
 pub struct UnifiedPipeline {
     view_layout: BindGroupLayout,
     types_layout: BindGroupLayout,
-    pub(crate) image_layout: BindGroupLayout,
+    pub(crate) font_image_layout: BindGroupLayout,
+    image_layout: BindGroupLayout,
     pipeline: CachedPipelineId,
     empty_font_texture: (GpuImage, BindGroup),
+    default_image: (GpuImage, BindGroup),
 }
 
 const QUAD_VERTEX_POSITIONS: &[Vec3] = &[
@@ -87,6 +93,33 @@ impl FromWorld for UnifiedPipeline {
             label: Some("ui_types_layout"),
         });
 
+        // Used by fonts
+        let font_image_layout =
+            render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
+                entries: &[
+                    BindGroupLayoutEntry {
+                        binding: 0,
+                        visibility: ShaderStages::FRAGMENT,
+                        ty: BindingType::Texture {
+                            multisampled: false,
+                            sample_type: TextureSampleType::Float { filterable: false },
+                            view_dimension: TextureViewDimension::D2Array,
+                        },
+                        count: None,
+                    },
+                    BindGroupLayoutEntry {
+                        binding: 1,
+                        visibility: ShaderStages::FRAGMENT,
+                        ty: BindingType::Sampler {
+                            comparison: false,
+                            filtering: true,
+                        },
+                        count: None,
+                    },
+                ],
+                label: Some("text_image_layout"),
+            });
+
         let image_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
             entries: &[
                 BindGroupLayoutEntry {
@@ -95,7 +128,7 @@ impl FromWorld for UnifiedPipeline {
                     ty: BindingType::Texture {
                         multisampled: false,
                         sample_type: TextureSampleType::Float { filterable: false },
-                        view_dimension: TextureViewDimension::D2Array,
+                        view_dimension: TextureViewDimension::D2,
                     },
                     count: None,
                 },
@@ -109,7 +142,7 @@ impl FromWorld for UnifiedPipeline {
                     count: None,
                 },
             ],
-            label: Some("text_image_layout"),
+            label: Some("image_layout"),
         });
 
         let vertex_buffer_layout = VertexBufferLayout {
@@ -139,7 +172,7 @@ impl FromWorld for UnifiedPipeline {
             ],
         };
 
-        let empty_font_texture = FontTextureCache::get_empty(&render_device, &image_layout);
+        let empty_font_texture = FontTextureCache::get_empty(&render_device, &font_image_layout);
 
         let pipeline_desc = RenderPipelineDescriptor {
             vertex: VertexState {
@@ -171,8 +204,9 @@ impl FromWorld for UnifiedPipeline {
             }),
             layout: Some(vec![
                 view_layout.clone(),
-                image_layout.clone(),
+                font_image_layout.clone(),
                 types_layout.clone(),
+                image_layout.clone(),
             ]),
             primitive: PrimitiveState {
                 front_face: FrontFace::Ccw,
@@ -189,20 +223,73 @@ impl FromWorld for UnifiedPipeline {
                 mask: !0,
                 alpha_to_coverage_enabled: false,
             },
-            label: Some("quad_pipeline".into()),
+            label: Some("unified_pipeline".into()),
         };
 
+        let texture_descriptor = TextureDescriptor {
+            label: Some("font_texture_array"),
+            size: Extent3d {
+                width: 1,
+                height: 1,
+                depth_or_array_layers: 1,
+            },
+            mip_level_count: 1,
+            sample_count: 1,
+            dimension: TextureDimension::D2,
+            format: TextureFormat::Rgba8UnormSrgb,
+            usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
+        };
+
+        let sampler_descriptor = SamplerDescriptor::default();
+
+        let texture = render_device.create_texture(&texture_descriptor);
+        let sampler = render_device.create_sampler(&sampler_descriptor);
+
+        let texture_view = texture.create_view(&TextureViewDescriptor {
+            label: Some("font_texture_array_view"),
+            format: None,
+            dimension: Some(TextureViewDimension::D2),
+            aspect: bevy::render2::render_resource::TextureAspect::All,
+            base_mip_level: 0,
+            base_array_layer: 0,
+            mip_level_count: None,
+            array_layer_count: None,
+        });
+
+        let image = GpuImage {
+            texture,
+            sampler,
+            texture_view,
+        };
+
+        let binding = render_device.create_bind_group(&BindGroupDescriptor {
+            label: Some("text_image_bind_group"),
+            entries: &[
+                BindGroupEntry {
+                    binding: 0,
+                    resource: BindingResource::TextureView(&image.texture_view),
+                },
+                BindGroupEntry {
+                    binding: 1,
+                    resource: BindingResource::Sampler(&image.sampler),
+                },
+            ],
+            layout: &image_layout,
+        });
+
         UnifiedPipeline {
             pipeline: pipeline_cache.queue(pipeline_desc),
             view_layout,
-            image_layout,
+            font_image_layout,
             empty_font_texture,
             types_layout,
+            image_layout,
+            default_image: (image, binding),
         }
     }
 }
 
-#[derive(Bundle)]
+#[derive(Debug, Bundle)]
 pub struct ExtractQuadBundle {
     pub(crate) extracted_quad: ExtractedQuad,
 }
@@ -211,9 +298,10 @@ pub struct ExtractQuadBundle {
 pub enum UIQuadType {
     Quad,
     Text,
+    Image,
 }
 
-#[derive(Component)]
+#[derive(Debug, Component, Clone)]
 pub struct ExtractedQuad {
     pub rect: Rect,
     pub color: Color,
@@ -224,6 +312,9 @@ pub struct ExtractedQuad {
     pub quad_type: UIQuadType,
     pub type_index: u32,
     pub border_radius: (f32, f32, f32, f32),
+    pub image: Option<Handle<Image>>,
+    pub uv_min: Option<Vec2>,
+    pub uv_max: Option<Vec2>,
 }
 
 #[repr(C)]
@@ -259,6 +350,11 @@ impl Default for QuadMeta {
     }
 }
 
+#[derive(Default)]
+pub struct ImageBindGroups {
+    values: HashMap<Handle<Image>, BindGroup>,
+}
+
 pub fn prepare_quads(
     render_device: Res<RenderDevice>,
     render_queue: Res<RenderQueue>,
@@ -266,7 +362,7 @@ pub fn prepare_quads(
     mut extracted_quads: Query<&mut ExtractedQuad>,
 ) {
     let extracted_sprite_len = extracted_quads.iter_mut().len();
-    // dont create buffers when there are no quads
+    // don't create buffers when there are no quads
     if extracted_sprite_len == 0 {
         return;
     }
@@ -275,6 +371,7 @@ pub fn prepare_quads(
     sprite_meta.types_buffer.reserve(2, &render_device);
     let quad_type_offset = sprite_meta.types_buffer.push(QuadType { t: 0 });
     let text_type_offset = sprite_meta.types_buffer.push(QuadType { t: 1 });
+    let image_type_offset = sprite_meta.types_buffer.push(QuadType { t: 2 });
     sprite_meta
         .types_buffer
         .write_buffer(&render_device, &render_queue);
@@ -292,29 +389,33 @@ pub fn prepare_quads(
         match extracted_sprite.quad_type {
             UIQuadType::Quad => extracted_sprite.type_index = quad_type_offset,
             UIQuadType::Text => extracted_sprite.type_index = text_type_offset,
+            UIQuadType::Image => extracted_sprite.type_index = image_type_offset,
         };
 
+        let uv_min = extracted_sprite.uv_min.unwrap_or(Vec2::ZERO);
+        let uv_max = extracted_sprite.uv_max.unwrap_or(Vec2::ONE);
+
         let bottom_left = Vec4::new(
-            0.0,
-            1.0,
+            uv_min.x,
+            uv_max.y,
             extracted_sprite.char_id as f32,
             extracted_sprite.border_radius.0,
         );
         let top_left = Vec4::new(
-            0.0,
-            0.0,
+            uv_min.x,
+            uv_min.y,
             extracted_sprite.char_id as f32,
             extracted_sprite.border_radius.1,
         );
         let top_right = Vec4::new(
-            1.0,
-            0.0,
+            uv_max.x,
+            uv_min.y,
             extracted_sprite.char_id as f32,
             extracted_sprite.border_radius.2,
         );
         let bottom_right = Vec4::new(
-            1.0,
-            1.0,
+            uv_max.x,
+            uv_max.y,
             extracted_sprite.char_id as f32,
             extracted_sprite.border_radius.3,
         );
@@ -362,6 +463,9 @@ pub fn queue_quads(
     quad_pipeline: Res<UnifiedPipeline>,
     mut extracted_sprites: Query<(Entity, &ExtractedQuad)>,
     mut views: Query<&mut RenderPhase<TransparentUI>>,
+    mut image_bind_groups: ResMut<ImageBindGroups>,
+    unified_pipeline: Res<UnifiedPipeline>,
+    gpu_images: Res<RenderAssets<Image>>,
 ) {
     if let Some(type_binding) = sprite_meta.types_buffer.binding() {
         sprite_meta.types_bind_group =
@@ -384,9 +488,34 @@ pub fn queue_quads(
             label: Some("quad_view_bind_group"),
             layout: &quad_pipeline.view_layout,
         }));
+
         let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap();
         for mut transparent_phase in views.iter_mut() {
             for (entity, quad) in extracted_sprites.iter_mut() {
+                if let Some(image_handle) = quad.image.as_ref() {
+                    image_bind_groups
+                        .values
+                        .entry(image_handle.clone_weak())
+                        .or_insert_with(|| {
+                            let gpu_image = gpu_images.get(&image_handle).unwrap();
+                            render_device.create_bind_group(&BindGroupDescriptor {
+                                entries: &[
+                                    BindGroupEntry {
+                                        binding: 0,
+                                        resource: BindingResource::TextureView(
+                                            &gpu_image.texture_view,
+                                        ),
+                                    },
+                                    BindGroupEntry {
+                                        binding: 1,
+                                        resource: BindingResource::Sampler(&gpu_image.sampler),
+                                    },
+                                ],
+                                label: Some("ui_image_bind_group"),
+                                layout: &unified_pipeline.image_layout,
+                            })
+                        });
+                }
                 transparent_phase.add(TransparentUI {
                     draw_function: draw_quad,
                     pipeline: quad_pipeline.pipeline,
@@ -404,6 +533,7 @@ pub struct DrawUI {
         SRes<UnifiedPipeline>,
         SRes<RenderPipelineCache>,
         SRes<FontTextureCache>,
+        SRes<ImageBindGroups>,
         SQuery<Read<ViewUniformOffset>>,
         SQuery<Read<ExtractedQuad>>,
     )>,
@@ -425,8 +555,16 @@ impl Draw<TransparentUI> for DrawUI {
         view: Entity,
         item: &TransparentUI,
     ) {
-        let (quad_meta, unified_pipeline, pipelines, font_texture_cache, views, quads) =
-            self.params.get(world);
+        let (
+            quad_meta,
+            unified_pipeline,
+            pipelines,
+            font_texture_cache,
+            image_bind_groups,
+            views,
+            quads,
+        ) = self.params.get(world);
+
         let view_uniform = views.get(view).unwrap();
         let quad_meta = quad_meta.into_inner();
         let extracted_quad = quads.get(item.entity).unwrap();
@@ -445,20 +583,31 @@ impl Draw<TransparentUI> for DrawUI {
                 &[extracted_quad.type_index],
             );
 
+            let unified_pipeline = unified_pipeline.into_inner();
             if let Some(font_handle) = extracted_quad.font_handle.as_ref() {
                 if let Some(image_bindings) =
                     font_texture_cache.into_inner().bind_groups.get(font_handle)
                 {
                     pass.set_bind_group(1, image_bindings, &[]);
                 } else {
-                    pass.set_bind_group(
-                        1,
-                        &unified_pipeline.into_inner().empty_font_texture.1,
-                        &[],
-                    );
+                    pass.set_bind_group(1, &unified_pipeline.empty_font_texture.1, &[]);
                 }
             } else {
-                pass.set_bind_group(1, &unified_pipeline.into_inner().empty_font_texture.1, &[]);
+                pass.set_bind_group(1, &unified_pipeline.empty_font_texture.1, &[]);
+            }
+
+            if let Some(image_handle) = extracted_quad.image.as_ref() {
+                pass.set_bind_group(
+                    3,
+                    &image_bind_groups
+                        .into_inner()
+                        .values
+                        .get(image_handle)
+                        .unwrap(),
+                    &[],
+                );
+            } else {
+                pass.set_bind_group(3, &unified_pipeline.default_image.1, &[]);
             }
 
             pass.draw(
diff --git a/bevy_kayak_ui/src/render/unified/quad/extract.rs b/bevy_kayak_ui/src/render/unified/quad/extract.rs
index 20a4c8ec13fefe9e1b58418ce777747b23dbe1ab..1a9fcf4ebb4a6e9aec7dd775cb8e8ea78c412002 100644
--- a/bevy_kayak_ui/src/render/unified/quad/extract.rs
+++ b/bevy_kayak_ui/src/render/unified/quad/extract.rs
@@ -47,6 +47,9 @@ pub fn extract_quads(mut commands: Commands, context: Res<BevyContext>) {
                 quad_type: UIQuadType::Quad,
                 type_index: 0,
                 border_radius: *border_radius,
+                image: None,
+                uv_max: None,
+                uv_min: None,
             },
         });
     }
diff --git a/bevy_kayak_ui/src/render/unified/shader.wgsl b/bevy_kayak_ui/src/render/unified/shader.wgsl
index 90a5af20ebd89422ce43604ca5bf70c367e7f87c..c32bbb8bba67c9980d61303ece5c64507e576933 100644
--- a/bevy_kayak_ui/src/render/unified/shader.wgsl
+++ b/bevy_kayak_ui/src/render/unified/shader.wgsl
@@ -13,7 +13,6 @@ struct QuadType {
 [[group(2), binding(0)]]
 var<uniform> quad_type: QuadType;
 
-
 struct VertexOutput {
     [[builtin(position)]] position: vec4<f32>;
     [[location(0)]] color: vec4<f32>;
@@ -39,17 +38,20 @@ fn vertex(
     out.uv = vertex_uv.xyz;
     out.size = vertex_pos_size.zw;
     out.border_radius = vertex_uv.w;
-
     return out;
 }
 
 [[group(1), binding(0)]]
-var sprite_texture: texture_2d_array<f32>;
+var font_texture: texture_2d_array<f32>;
 [[group(1), binding(1)]]
-var sprite_sampler: sampler;
+var font_sampler: sampler;
 
-let RADIUS: f32 = 0.1;
+[[group(3), binding(0)]]
+var image_texture: texture_2d<f32>;
+[[group(3), binding(1)]]
+var image_sampler: sampler;
 
+let RADIUS: f32 = 0.1;
 
 fn sd_box_rounded(
     frag_coord: vec2<f32>,
@@ -71,9 +73,6 @@ fn sd_box_rounded(
 
 [[stage(fragment)]]
 fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
-    var pxRange = 2.5;
-    var tex_dimensions = textureDimensions(sprite_texture);
-    var msdfUnit = vec2<f32>(pxRange, pxRange) / vec2<f32>(f32(tex_dimensions.x), f32(tex_dimensions.y));
     if (quad_type.t == 0) {
         var dist = sd_box_rounded(
             in.position.xy,
@@ -89,7 +88,10 @@ fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
         return vec4<f32>(in.color.rgb, dist);
     }
     if (quad_type.t == 1) {
-        var x = textureSample(sprite_texture, sprite_sampler, vec2<f32>(in.uv.x, 1.0 - in.uv.y), i32(in.uv.z)); 
+        var px_range = 2.5;
+        var tex_dimensions = textureDimensions(font_texture);
+        var msdf_unit = vec2<f32>(px_range, px_range) / vec2<f32>(f32(tex_dimensions.x), f32(tex_dimensions.y));
+        var x = textureSample(font_texture, font_sampler, vec2<f32>(in.uv.x, 1.0 - in.uv.y), i32(in.uv.z)); 
         var v = max(min(x.r, x.g), min(max(x.r, x.g), x.b));
         var c = v; //remap(v);
 
@@ -103,10 +105,14 @@ fn fragment(in: VertexOutput) -> [[location(0)]] vec4<f32> {
         // var w = fwidth(c);
         // var a = smoothStep(0.5 - w, 0.5 + w, c);
 
-        var sigDist = (c - 0.5) * dot(msdfUnit, 0.5 / fwidth(in.uv.xy));
-        var a = clamp(sigDist + 0.5, 0.0, 1.0);
+        var sig_dist = (c - 0.5) * dot(msdf_unit, 0.5 / fwidth(in.uv.xy));
+        var a = clamp(sig_dist + 0.5, 0.0, 1.0);
 
         return vec4<f32>(in.color.rgb, a);
     }
+    if (quad_type.t == 2) {
+        var color = textureSample(image_texture, image_sampler, vec2<f32>(in.uv.x, in.uv.y));
+        return vec4<f32>(color.rgb * in.color.rgb, color.a * in.color.a);
+    }
     return in.color;
 }
\ No newline at end of file
diff --git a/examples/counter.rs b/examples/counter.rs
index cc68c29f9d3e098742bb8abda0c0612ba704cb7e..2d946636fd5a0c8df3f5ea2a756004935f95e975 100644
--- a/examples/counter.rs
+++ b/examples/counter.rs
@@ -7,6 +7,7 @@ use bevy::{
 use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, UICameraBundle};
 use kayak_components::{Button, Text, Window};
 use kayak_core::{
+    context::KayakContext,
     styles::{Style, StyleProp, Units},
     EventType, Index, OnEvent,
 };
@@ -14,7 +15,7 @@ use kayak_ui::components::App;
 use kayak_ui::core::{rsx, widget};
 
 #[widget]
-fn Counter() {
+fn Counter(context: &mut KayakContext) {
     let count = {
         let x = context.create_state(0i32).unwrap();
         *x
diff --git a/examples/full_ui.rs b/examples/full_ui.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ed9b075b7d9ab309ec9cbce1c64d52c74e627ba2
--- /dev/null
+++ b/examples/full_ui.rs
@@ -0,0 +1,197 @@
+use bevy::{
+    math::Vec2,
+    prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut, World},
+    window::{WindowDescriptor, Windows},
+    PipelinedDefaultPlugins,
+};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
+use kayak_components::{NinePatch, Text};
+use kayak_core::{
+    context::KayakContext,
+    layout_cache::Space,
+    styles::{LayoutType, Style, StyleProp, Units},
+    widget, Children, EventType, Index, OnEvent,
+};
+use kayak_ui::components::App;
+use kayak_ui::core::rsx;
+
+#[widget]
+fn BlueButton(context: KayakContext, children: Children, styles: Option<Style>) {
+    let (blue_button_handle, blue_button_hover_handle) = {
+        let world = context.get_global_state::<World>();
+        if world.is_err() {
+            return;
+        }
+
+        let mut world = world.unwrap();
+
+        let (handle1, handle2) = {
+            let asset_server = world.get_resource::<AssetServer>().unwrap();
+            let handle1: Handle<bevy::render2::texture::Image> =
+                asset_server.load("../assets/kenny/buttonSquare_blue.png");
+            let handle2: Handle<bevy::render2::texture::Image> =
+                asset_server.load("../assets/kenny/buttonSquare_blue_pressed.png");
+
+            (handle1, handle2)
+        };
+
+        let mut image_manager = world.get_resource_mut::<ImageManager>().unwrap();
+        let blue_button_handle = image_manager.get(&handle1);
+        let blue_button_hover_handle = image_manager.get(&handle2);
+
+        (blue_button_handle, blue_button_hover_handle)
+    };
+
+    let current_button_handle = *context.create_state::<u16>(blue_button_handle).unwrap();
+    dbg!(current_button_handle);
+
+    let button_styles = Style {
+        width: StyleProp::Value(Units::Pixels(200.0)),
+        height: StyleProp::Value(Units::Pixels(50.0)),
+        padding_left: StyleProp::Value(Units::Stretch(1.0)),
+        padding_right: StyleProp::Value(Units::Stretch(1.0)),
+        padding_top: StyleProp::Value(Units::Stretch(1.0)),
+        padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
+        ..styles.clone().unwrap_or_default()
+    };
+
+    let button_id = self.get_id();
+    let on_event = OnEvent::new(move |context, event| match event.event_type {
+        EventType::Click => {
+            dbg!("Clicked!");
+            context.set_current_id(button_id);
+            context.set_state::<u16>(blue_button_hover_handle);
+        }
+        _ => {
+            context.set_state::<u16>(blue_button_handle);
+        }
+    });
+
+    rsx! {
+        <NinePatch
+            border={Space {
+                left: 10.0,
+                right: 10.0,
+                top: 10.0,
+                bottom: 10.0,
+            }}
+            handle={current_button_handle}
+            styles={Some(button_styles)}
+            on_event={Some(on_event)}
+        >
+            {children}
+        </NinePatch>
+    }
+}
+
+fn startup(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    asset_server: Res<AssetServer>,
+    mut image_manager: ResMut<ImageManager>,
+) {
+    commands.spawn_bundle(UICameraBundle::new());
+
+    let window_size = if let Some(window) = windows.get_primary() {
+        Vec2::new(window.width(), window.height())
+    } else {
+        panic!("Couldn't find primary window!");
+    };
+
+    let handle: Handle<bevy::render2::texture::Image> = asset_server.load("kenny/panel_brown.png");
+    let panel_brown_handle = image_manager.get(&handle);
+
+    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+        // Hack to trick the proc macro for right now..
+        let parent_id: Option<Index> = None;
+
+        let nine_patch_styles = Style {
+            layout_type: StyleProp::Value(LayoutType::Column),
+            width: StyleProp::Value(Units::Pixels(512.0)),
+            height: StyleProp::Value(Units::Pixels(512.0)),
+            padding_left: StyleProp::Value(Units::Stretch(1.0)),
+            padding_right: StyleProp::Value(Units::Stretch(1.0)),
+            padding_top: StyleProp::Value(Units::Stretch(1.0)),
+            padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
+            ..Style::default()
+        };
+
+        let app_styles = Style {
+            padding_left: StyleProp::Value(Units::Stretch(1.0)),
+            padding_right: StyleProp::Value(Units::Stretch(1.0)),
+            padding_top: StyleProp::Value(Units::Stretch(1.0)),
+            padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
+            ..styles.clone()
+        };
+
+        let header_styles = Style {
+            width: StyleProp::Value(Units::Pixels(432.0)),
+            height: StyleProp::Value(Units::Pixels(64.0)),
+            bottom: StyleProp::Value(Units::Stretch(1.0)),
+            ..Style::default()
+        };
+
+        let play_button_styles = Style {
+            width: StyleProp::Value(Units::Pixels(54.0)),
+            height: StyleProp::Value(Units::Pixels(45.0)),
+            ..Style::default()
+        };
+
+        let options_button_text_styles = Style {
+            width: StyleProp::Value(Units::Pixels(102.0)),
+            height: StyleProp::Value(Units::Pixels(45.0)),
+            ..Style::default()
+        };
+
+        let options_button_styles = Style {
+            top: StyleProp::Value(Units::Pixels(15.0)),
+            ..Style::default()
+        };
+
+        rsx! {
+            <App styles={Some(app_styles)}>
+                <NinePatch
+                    styles={Some(nine_patch_styles)}
+                    border={Space {
+                        left: 30.0,
+                        right: 30.0,
+                        top: 30.0,
+                        bottom: 30.0,
+                    }}
+                    handle={panel_brown_handle}
+                >
+                    <Text
+                        styles={Some(header_styles)}
+                        size={50.0}
+                        content={"Name My Game Plz".to_string()}
+                    />
+                    <BlueButton>
+                        <Text styles={Some(play_button_styles)} size={30.0} content={"Play".to_string()} />
+                    </BlueButton>
+                    <BlueButton styles={Some(options_button_styles)}>
+                        <Text styles={Some(options_button_text_styles)} size={30.0} content={"Options".to_string()} />
+                    </BlueButton>
+                    <BlueButton styles={Some(options_button_styles)}>
+                        <Text styles={Some(play_button_styles)} size={30.0} content={"Quit".to_string()} />
+                    </BlueButton>
+                </NinePatch>
+            </App>
+        }
+    });
+
+    commands.insert_resource(context);
+}
+
+fn main() {
+    BevyApp::new()
+        .insert_resource(WindowDescriptor {
+            width: 1270.0,
+            height: 720.0,
+            title: String::from("UI Example"),
+            ..Default::default()
+        })
+        .add_plugins(PipelinedDefaultPlugins)
+        .add_plugin(BevyKayakUIPlugin)
+        .add_startup_system(startup)
+        .run();
+}
diff --git a/examples/image.rs b/examples/image.rs
new file mode 100644
index 0000000000000000000000000000000000000000..82b571488c00812cdf44bd7713a2d89daec7beed
--- /dev/null
+++ b/examples/image.rs
@@ -0,0 +1,55 @@
+use bevy::{
+    math::Vec2,
+    prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
+    window::{WindowDescriptor, Windows},
+    PipelinedDefaultPlugins,
+};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
+use kayak_components::Image;
+use kayak_core::Index;
+use kayak_ui::components::App;
+use kayak_ui::core::rsx;
+
+fn startup(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    asset_server: Res<AssetServer>,
+    mut image_manager: ResMut<ImageManager>,
+) {
+    commands.spawn_bundle(UICameraBundle::new());
+
+    let window_size = if let Some(window) = windows.get_primary() {
+        Vec2::new(window.width(), window.height())
+    } else {
+        panic!("Couldn't find primary window!");
+    };
+
+    let handle: Handle<bevy::render2::texture::Image> = asset_server.load("panel.png");
+    let ui_image_handle = image_manager.get(&handle);
+
+    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+        // Hack to trick the proc macro for right now..
+        let parent_id: Option<Index> = None;
+        rsx! {
+            <App styles={Some(styles.clone())}>
+                <Image handle={ui_image_handle} />
+            </App>
+        }
+    });
+
+    commands.insert_resource(context);
+}
+
+fn main() {
+    BevyApp::new()
+        .insert_resource(WindowDescriptor {
+            width: 1270.0,
+            height: 720.0,
+            title: String::from("UI Example"),
+            ..Default::default()
+        })
+        .add_plugins(PipelinedDefaultPlugins)
+        .add_plugin(BevyKayakUIPlugin)
+        .add_startup_system(startup)
+        .run();
+}
diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0a14f1a27c83ee6cbbd0787d724d51b7e6216c88
--- /dev/null
+++ b/examples/nine_patch.rs
@@ -0,0 +1,98 @@
+use bevy::{
+    math::Vec2,
+    prelude::{App as BevyApp, AssetServer, Commands, Handle, Res, ResMut},
+    window::{WindowDescriptor, Windows},
+    PipelinedDefaultPlugins,
+};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, ImageManager, UICameraBundle};
+use kayak_components::NinePatch;
+use kayak_core::{
+    layout_cache::Space,
+    styles::{Style, StyleProp, Units},
+    Index,
+};
+use kayak_ui::components::App;
+use kayak_ui::core::rsx;
+
+fn startup(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    asset_server: Res<AssetServer>,
+    mut image_manager: ResMut<ImageManager>,
+) {
+    commands.spawn_bundle(UICameraBundle::new());
+
+    let window_size = if let Some(window) = windows.get_primary() {
+        Vec2::new(window.width(), window.height())
+    } else {
+        panic!("Couldn't find primary window!");
+    };
+
+    let handle: Handle<bevy::render2::texture::Image> = asset_server.load("panel.png");
+    let ui_image_handle = image_manager.get(&handle);
+
+    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+        // Hack to trick the proc macro for right now..
+        let parent_id: Option<Index> = None;
+
+        // The border prop splits up the image into 9 quadrants like so:
+        // 1----2----3
+        // |         |
+        // 4    9    5
+        // |         |
+        // 6----7----8
+        // The sizes of sprites for a 15 pixel border are as follows:
+        // TopLeft = (15, 15)
+        // TopRight = (15, 15)
+        // LeftCenter = (15, image_height)
+        // RightCenter = (15, image_height)
+        // TopCenter = (image_width, 15)
+        // BottomCenter = (image_width, 15)
+        // BottomRight = (15, 15)
+        // BottomLeft = (15, 15)
+        // Middle = (
+        // 30 being left border + right border
+        //   image_width - 30
+        // 30 being top border + bottom border
+        //   image_height - 30
+        // )
+        //
+
+        let nine_patch_styles = Style {
+            width: StyleProp::Value(Units::Pixels(512.0)),
+            height: StyleProp::Value(Units::Pixels(512.0)),
+            ..Style::default()
+        };
+
+        rsx! {
+            <App styles={Some(styles.clone())}>
+                <NinePatch
+                    styles={Some(nine_patch_styles)}
+                    border={Space {
+                        left: 15.0,
+                        right: 15.0,
+                        top: 15.0,
+                        bottom: 15.0,
+                    }}
+                    handle={ui_image_handle}
+                />
+            </App>
+        }
+    });
+
+    commands.insert_resource(context);
+}
+
+fn main() {
+    BevyApp::new()
+        .insert_resource(WindowDescriptor {
+            width: 1270.0,
+            height: 720.0,
+            title: String::from("UI Example"),
+            ..Default::default()
+        })
+        .add_plugins(PipelinedDefaultPlugins)
+        .add_plugin(BevyKayakUIPlugin)
+        .add_startup_system(startup)
+        .run();
+}
diff --git a/kayak_components/src/image.rs b/kayak_components/src/image.rs
new file mode 100644
index 0000000000000000000000000000000000000000..39cd65c8b5d4c25aa3c4edf30987d5b1ae440dd8
--- /dev/null
+++ b/kayak_components/src/image.rs
@@ -0,0 +1,20 @@
+use kayak_core::{
+    render_command::RenderCommand,
+    rsx,
+    styles::{Style, StyleProp},
+    widget, Children,
+};
+
+#[widget]
+pub fn Image(handle: u16, children: Children) {
+    *styles = Some(Style {
+        render_command: StyleProp::Value(RenderCommand::Image { handle: *handle }),
+        ..styles.clone().unwrap_or_default()
+    });
+
+    rsx! {
+        <>
+            {children}
+        </>
+    }
+}
diff --git a/kayak_components/src/lib.rs b/kayak_components/src/lib.rs
index a9b99766d6e61c29c1503cd17f00736729100b40..d59d930cb5444d0dce0afb700c63c784fa066f6e 100644
--- a/kayak_components/src/lib.rs
+++ b/kayak_components/src/lib.rs
@@ -2,6 +2,8 @@ mod app;
 mod background;
 mod button;
 mod clip;
+mod image;
+mod nine_patch;
 mod text;
 mod window;
 
@@ -9,5 +11,7 @@ pub use app::*;
 pub use background::*;
 pub use button::*;
 pub use clip::*;
+pub use image::*;
+pub use nine_patch::*;
 pub use text::*;
 pub use window::*;
diff --git a/kayak_components/src/nine_patch.rs b/kayak_components/src/nine_patch.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8d652ab6ebd018b6f4de826fa0b99e491e2318b5
--- /dev/null
+++ b/kayak_components/src/nine_patch.rs
@@ -0,0 +1,24 @@
+use kayak_core::{
+    layout_cache::Space,
+    render_command::RenderCommand,
+    rsx,
+    styles::{Style, StyleProp},
+    widget, Children,
+};
+
+#[widget]
+pub fn NinePatch(handle: u16, border: Space, children: Children) {
+    *styles = Some(Style {
+        render_command: StyleProp::Value(RenderCommand::NinePatch {
+            handle: *handle,
+            border: *border,
+        }),
+        ..styles.clone().unwrap_or_default()
+    });
+
+    rsx! {
+        <>
+            {children}
+        </>
+    }
+}
diff --git a/kayak_core/cargo.toml b/kayak_core/Cargo.toml
similarity index 100%
rename from kayak_core/cargo.toml
rename to kayak_core/Cargo.toml
diff --git a/kayak_core/src/context.rs b/kayak_core/src/context.rs
index b18aa0ef8c79a7e4d7ea06e36515673cc6e77441..59d1d52306c005762e0cd1b2e17ee12983186f25 100644
--- a/kayak_core/src/context.rs
+++ b/kayak_core/src/context.rs
@@ -1,6 +1,5 @@
-use std::collections::HashMap;
-
 use resources::Ref;
+use std::collections::HashMap;
 
 use crate::{node::NodeIndex, widget_manager::WidgetManager, Event, EventType, Index, InputEvent};
 
@@ -9,6 +8,7 @@ pub struct KayakContext {
     current_id: crate::Index,
     pub widget_manager: WidgetManager,
     last_mouse_position: (f32, f32),
+    pub global_state: resources::Resources,
 }
 
 impl std::fmt::Debug for KayakContext {
@@ -26,6 +26,7 @@ impl KayakContext {
             current_id: crate::Index::default(),
             widget_manager: WidgetManager::new(),
             last_mouse_position: (0.0, 0.0),
+            global_state: resources::Resources::default(),
         }
     }
 
@@ -37,6 +38,7 @@ impl KayakContext {
         &mut self,
         initial_state: T,
     ) -> Option<Ref<T>> {
+        dbg!(self.current_id);
         if self.component_states.contains_key(&self.current_id) {
             let states = self.component_states.get_mut(&self.current_id).unwrap();
             if !states.contains::<T>() {
@@ -61,10 +63,12 @@ impl KayakContext {
     }
 
     pub fn set_state<T: resources::Resource + Clone>(&mut self, state: T) {
+        dbg!(self.current_id);
         if self.component_states.contains_key(&self.current_id) {
             let states = self.component_states.get(&self.current_id).unwrap();
             if states.contains::<T>() {
                 let mut mutate_t = states.get_mut::<T>().unwrap();
+                dbg!("Mutating state!");
                 self.widget_manager.dirty_nodes.push(self.current_id);
                 *mutate_t = state;
             } else {
@@ -78,6 +82,20 @@ impl KayakContext {
         }
     }
 
+    pub fn set_global_state<T: resources::Resource>(&mut self, state: T) {
+        self.global_state.insert(state);
+    }
+
+    pub fn get_global_state<T: resources::Resource>(
+        &mut self,
+    ) -> Result<resources::RefMut<T>, resources::CantGetResource> {
+        self.global_state.get_mut::<T>()
+    }
+
+    pub fn take_global_state<T: resources::Resource>(&mut self) -> Option<T> {
+        self.global_state.remove::<T>()
+    }
+
     pub fn render(&mut self) {
         let dirty_nodes = self.widget_manager.dirty_nodes.clone();
         for node_index in dirty_nodes {
diff --git a/kayak_core/src/layout_cache.rs b/kayak_core/src/layout_cache.rs
index c843be7332f35973d4dd37bd0a0d705ab5925962..9b31c08c8b4bfdedb474ceec4c6843787e504380 100644
--- a/kayak_core/src/layout_cache.rs
+++ b/kayak_core/src/layout_cache.rs
@@ -20,7 +20,7 @@ impl Rect {
     }
 }
 
-#[derive(Debug, Default, Clone, Copy)]
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
 pub struct Space {
     pub left: f32,
     pub right: f32,
diff --git a/kayak_core/src/lib.rs b/kayak_core/src/lib.rs
index 47009cc660238e08faefea66b6ec6915f67e2674..fe2cc7fdd442504763f3577a545d658567a24908 100644
--- a/kayak_core/src/lib.rs
+++ b/kayak_core/src/lib.rs
@@ -22,6 +22,8 @@ pub use input_event::*;
 pub use kayak_render_macros::{render, rsx, widget};
 pub use widget::Widget;
 
+pub use resources::Resources;
+
 pub type Children =
     Option<Arc<dyn Fn(Option<crate::Index>, &mut crate::context::KayakContext) + Send + Sync>>;
 
diff --git a/kayak_core/src/render_command.rs b/kayak_core/src/render_command.rs
index 31f034efcb8c6cbb7cd804da0fa2984288b605e4..c717a2f23aa8706a9bb8a17c163f1fdb668c59f4 100644
--- a/kayak_core/src/render_command.rs
+++ b/kayak_core/src/render_command.rs
@@ -1,3 +1,5 @@
+use crate::layout_cache::Space;
+
 #[derive(Debug, Clone, PartialEq)]
 pub enum RenderCommand {
     Empty,
@@ -9,6 +11,13 @@ pub enum RenderCommand {
         size: f32,
         font: u16,
     },
+    Image {
+        handle: u16,
+    },
+    NinePatch {
+        border: Space,
+        handle: u16,
+    },
 }
 
 impl Default for RenderCommand {
diff --git a/kayak_core/src/render_primitive.rs b/kayak_core/src/render_primitive.rs
index 3208f6c1f4fe58fe4f16149fb7e02d5fd279659d..54b3224809697e9b06401be84786874482bed734 100644
--- a/kayak_core/src/render_primitive.rs
+++ b/kayak_core/src/render_primitive.rs
@@ -1,6 +1,6 @@
 use crate::{
     color::Color,
-    layout_cache::Rect,
+    layout_cache::{Rect, Space},
     render_command::RenderCommand,
     styles::{Style, StyleProp},
 };
@@ -23,6 +23,15 @@ pub enum RenderPrimitive {
         content: String,
         font: u16,
     },
+    Image {
+        layout: Rect,
+        handle: u16,
+    },
+    NinePatch {
+        border: Space,
+        layout: Rect,
+        handle: u16,
+    },
 }
 
 impl RenderPrimitive {
@@ -31,6 +40,8 @@ impl RenderPrimitive {
             RenderPrimitive::Clip { layout, .. } => *layout = new_layout,
             RenderPrimitive::Quad { layout, .. } => *layout = new_layout,
             RenderPrimitive::Text { layout, .. } => *layout = new_layout,
+            RenderPrimitive::Image { layout, .. } => *layout = new_layout,
+            RenderPrimitive::NinePatch { layout, .. } => *layout = new_layout,
             _ => (),
         }
     }
@@ -68,6 +79,15 @@ impl From<&Style> for RenderPrimitive {
                 content,
                 font,
             },
+            RenderCommand::Image { handle } => Self::Image {
+                layout: Rect::default(),
+                handle,
+            },
+            RenderCommand::NinePatch { handle, border } => Self::NinePatch {
+                border,
+                layout: Rect::default(),
+                handle,
+            },
         }
     }
 }
diff --git a/kayak_core/src/styles.rs b/kayak_core/src/styles.rs
index 17266dc971130ef194237f890258de14561921b6..a1671dcf387ec682d5e48b2cb8dd3d6f20d7de92 100644
--- a/kayak_core/src/styles.rs
+++ b/kayak_core/src/styles.rs
@@ -49,6 +49,10 @@ pub struct Style {
     pub padding_right: StyleProp<Units>,
     pub padding_top: StyleProp<Units>,
     pub padding_bottom: StyleProp<Units>,
+    pub margin_left: StyleProp<Units>,
+    pub margin_right: StyleProp<Units>,
+    pub margin_top: StyleProp<Units>,
+    pub margin_bottom: StyleProp<Units>,
 }
 
 impl Default for Style {
@@ -70,6 +74,10 @@ impl Default for Style {
             padding_right: StyleProp::Default,
             padding_top: StyleProp::Default,
             padding_bottom: StyleProp::Default,
+            margin_left: StyleProp::Default,
+            margin_right: StyleProp::Default,
+            margin_top: StyleProp::Default,
+            margin_bottom: StyleProp::Default,
         }
     }
 }
@@ -164,5 +172,21 @@ impl Style {
             StyleProp::Inherit => self.padding_bottom = other.padding_bottom.clone(),
             _ => (),
         }
+        match self.margin_left {
+            StyleProp::Inherit => self.margin_left = other.margin_left.clone(),
+            _ => (),
+        }
+        match self.margin_right {
+            StyleProp::Inherit => self.margin_right = other.margin_right.clone(),
+            _ => (),
+        }
+        match self.margin_top {
+            StyleProp::Inherit => self.margin_top = other.margin_top.clone(),
+            _ => (),
+        }
+        match self.margin_bottom {
+            StyleProp::Inherit => self.margin_bottom = other.margin_bottom.clone(),
+            _ => (),
+        }
     }
 }
diff --git a/kayak_core/src/widget_manager.rs b/kayak_core/src/widget_manager.rs
index 97ed85ebe3fdb3f78e6f8eab4e98726d97f87058..dd141b2000b077425f0792b647b780af24ebd17e 100644
--- a/kayak_core/src/widget_manager.rs
+++ b/kayak_core/src/widget_manager.rs
@@ -139,6 +139,7 @@ impl WidgetManager {
             right: crate::styles::StyleProp::Default,
             top: crate::styles::StyleProp::Default,
             width: crate::styles::StyleProp::Default,
+            ..Style::default()
         };
         for dirty_node_index in self.dirty_render_nodes.drain(..) {
             let dirty_widget = self.current_widgets[dirty_node_index].as_ref().unwrap();
diff --git a/kayak_render_macros/src/children.rs b/kayak_render_macros/src/children.rs
index f948e6727cb1817f9b96c964ab349c6d73ab9471..88dd8dc2c2a1054ae1cc984d18162f0a75749c86 100644
--- a/kayak_render_macros/src/children.rs
+++ b/kayak_render_macros/src/children.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
 use crate::{
     arc_function::build_arc_function,
     attribute::Attribute,
@@ -95,60 +97,101 @@ impl Children {
             _ => {
                 let mut iter = children_quotes.iter();
 
-                let first = iter.next().unwrap();
-                let second = iter.next().unwrap();
-
-                let first = build_arc_function(quote! { child1 }, first.clone(), true, 0);
-                let second = build_arc_function(quote! { child2 }, second.clone(), true, 1);
+                // First get shared and non-shared attributes..
+                let mut child_attributes_list = Vec::new();
+                for i in 0..children_quotes.len() {
+                    child_attributes_list.push(self.get_clonable_attributes(i));
+                }
 
-                let children_attributes0: Vec<_> = self.get_clonable_attributes(0);
-                let children_attributes1: Vec<_> = self.get_clonable_attributes(1);
-                let (children_attributes0, children_attributes1, matching) =
-                    handle_tuple_attributes(&children_attributes0, &children_attributes1);
+                let mut all_attributes = HashSet::new();
+                for child_attributes in child_attributes_list.iter() {
+                    for child_attribute in child_attributes {
+                        all_attributes.insert(child_attribute.to_string());
+                    }
+                }
 
-                let base_matching: Vec<proc_macro2::TokenStream> = matching
+                let base_matching: Vec<proc_macro2::TokenStream> = all_attributes
                     .iter()
-                    .map(|a| {
-                        format!("base_{}", a.to_string())
-                            .to_string()
-                            .parse()
-                            .unwrap()
-                    })
+                    .map(|a| format!("base_{}", a).to_string().parse().unwrap())
                     .collect();
 
+                let all_attributes: Vec<proc_macro2::TokenStream> =
+                    all_attributes.iter().map(|a| a.parse().unwrap()).collect();
+
                 let base_clone = quote! {
-                    #(let #base_matching = #matching.clone();)*
+                    #(let #base_matching = #all_attributes.clone();)*
                 };
 
                 let base_clones_inner = quote! {
-                    #(let #matching = #base_matching.clone();)*
+                    #(let #all_attributes = #base_matching.clone();)*
                 };
 
-                let cloned_attrs0 = quote! {
-                    #(let #children_attributes0 = #children_attributes0.clone();)*
-                };
-                let cloned_attrs1 = quote! {
-                    #(let #children_attributes1 = #children_attributes1.clone();)*
-                };
+                let mut output = Vec::new();
+                output.push(quote! { #base_clone });
+                for i in 0..children_quotes.len() {
+                    output.push(quote! { #base_clones_inner });
+                    let name: proc_macro2::TokenStream = format!("child{}", i).parse().unwrap();
+                    let child =
+                        build_arc_function(quote! { #name }, children_quotes[i].clone(), true, i);
+                    output.push(quote! { #child });
+                }
 
-                let tuple_of_tuples = iter.fold(
-                    quote! {
-                        #base_clone
-                        #cloned_attrs0
-                        #base_clones_inner
-                        #first
-                        #base_clones_inner
-                        #cloned_attrs1
-                        #second
-                    },
-                    |renderable, current| quote!((#renderable, #current)),
-                );
+                // let first = iter.next().unwrap();
+                // let second = iter.next().unwrap();
+
+                // let first = build_arc_function(quote! { child1 }, first.clone(), true, 0);
+                // let second = build_arc_function(quote! { child2 }, second.clone(), true, 1);
+
+                // let children_attributes0: Vec<_> = self.get_clonable_attributes(0);
+                // let children_attributes1: Vec<_> = self.get_clonable_attributes(1);
+                // let (children_attributes0, children_attributes1, matching) =
+                //     handle_tuple_attributes(&children_attributes0, &children_attributes1);
+
+                // let base_matching: Vec<proc_macro2::TokenStream> = matching
+                //     .iter()
+                //     .map(|a| {
+                //         format!("base_{}", a.to_string())
+                //             .to_string()
+                //             .parse()
+                //             .unwrap()
+                //     })
+                //     .collect();
+
+                // let base_clone = quote! {
+                //     #(let #base_matching = #matching.clone();)*
+                // };
+
+                // let base_clones_inner = quote! {
+                //     #(let #matching = #base_matching.clone();)*
+                // };
+
+                // let cloned_attrs0 = quote! {
+                //     #(let #children_attributes0 = #children_attributes0.clone();)*
+                // };
+                // let cloned_attrs1 = quote! {
+                //     #(let #children_attributes1 = #children_attributes1.clone();)*
+                // };
+
+                // let tuple_of_tuples = iter.fold(
+                //     quote! {
+                //         #base_clone
+                //         #cloned_attrs0
+                //         #base_clones_inner
+                //         #first
+                //         #base_clones_inner
+                //         #cloned_attrs1
+                //         #second
+                //     },
+                //     |renderable, current| quote!((#renderable, #current)),
+                // );
 
                 quote! {
                     Some(std::sync::Arc::new(move |parent_id: Option<kayak_core::Index>, context: &mut kayak_core::context::KayakContext| {
-                        #tuple_of_tuples
+                        #(#output)*
                     }))
                 }
+
+                // quote! {}
             }
         }
     }
diff --git a/kayak_render_macros/src/widget.rs b/kayak_render_macros/src/widget.rs
index 96d0c703d7621ead8ad5486a71f92e408038bbb9..1368e1875e472706f59f014cb63c7f042cde7c8e 100644
--- a/kayak_render_macros/src/widget.rs
+++ b/kayak_render_macros/src/widget.rs
@@ -10,7 +10,6 @@ use crate::{tags::OpenTag, widget_attributes::WidgetAttributes};
 
 #[derive(Debug, Clone)]
 pub struct Widget {
-    name: syn::Path,
     pub attributes: WidgetAttributes,
     pub children: Children,
     declaration: TokenStream,
@@ -82,7 +81,6 @@ impl Widget {
         };
 
         Ok(Widget {
-            name,
             attributes: open_tag.attributes,
             children,
             declaration,