From 5eef4096da1fc2e208dd23760efa27cfdae8a0c3 Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Tue, 9 Dec 2025 17:50:17 -0800 Subject: [PATCH] touch ups --- frontend/index.html | 15 +++- frontend/public/tic-tac-toe.png | Bin 0 -> 17566 bytes frontend/src/App.tsx | 99 +++++++++++++--------- frontend/src/components/Navbar/Navbar.tsx | 28 +++--- frontend/src/config/apps.ts | 10 ++- frontend/src/pages/LandingPage.tsx | 19 +++++ frontend/vite.config.ts | 16 ++++ 7 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 frontend/public/tic-tac-toe.png diff --git a/frontend/index.html b/frontend/index.html index 89b5c40..1843ea6 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,9 +4,19 @@ + - + + + + + + + + + + Rainbow, Cupcakes & Unicorns - Free Games for Kids @@ -14,6 +24,3 @@ - - - diff --git a/frontend/public/tic-tac-toe.png b/frontend/public/tic-tac-toe.png new file mode 100644 index 0000000000000000000000000000000000000000..230e556db03ea9c7054115200206c618ce9512b6 GIT binary patch literal 17566 zcmc(nWmuE{+wiY}h#;YeU{DI9OAwJp5a}+FR7zT4fTK1Oksc-8EubKsKMO{8jZhh# zjuB&HdoF(e`+oDhdtTi4!*Ou1W7jv&_{QfvFCvW$G#Tly(E|X$sI7J11OTYOk5s_9 zv)~{6^RZ*_58X2@D}Mk8lze@@4t{wRrtuJF>f-_ndhF*61O)|&yLo%~J3fBq zEbimynz5~T4FGrm?fdu4o@Z{&!9UnIXC3?{29+nm(l31XRj0``Ha;`Cs$hJNn#Cyo z-e0JEY;+AYmrqr#q*lJ`g(&COlym98c3-sQqT0jPx%VNAJ6C?r8Q-^>V`tC~5||4J z-{Ec>a%)zw9bXyqFCRGcx3ys(FAHAZt}@6bDh`&JvVep9pZp1N&7%gAccUg+Zzp6u zUY1@8e!O7G9n{dDKq&EqV+OzS-{;+9t_+2n#2QEw^t2_Cuqdto!gA>>UWnN6hWSkF zx#+7jK)^qXjY4kev)(mLD1Lmn?`qcAV%7fe-hoH0oPVBwY7wBjj<2S+@J2|Lqp6OWvdEUAV9IyT z`m=^)N7)-fny!Uev|5+IiMW_Rb*n>^CpM>U*XBO&6Ml5Ou=kmz1JPaQ9{X_}g>dPQ zS}K}ECtB(rn55YWPVVt3skbqzB#0#71)5F_61dE*R1#3!W$2ySQ8sdYF?_N?#oj%* z;3lUyAFgcy;$SifH-8I^$zqfNMJ318ViTSo1{plX+`+ z;A&L*NO+|YLwFA()Cc~_m4o7)RItI$zRYuezvOqg+=+B5CqHyWc%7^XovChEEcD|c zNj2H=Pix{QHN$r%Kb|CEuWx;vu#XYh5fDctD5h1aDEc+`WoJBsN-Jq@?gF z!_kNySaQ}vu)WS80F*j1ngXQS)DB~vC+N6j(IPp-{lSDUS~YLleYe<)p1$eNrY!25 z-K`K;F*AmjwUU&zi=ro=@b|*X3r$bn>$dYwcWsi+`wZf*LVzZVLz>QN`#qE74DnRd zC8=zD#3k)xO(A%-j}7sv)hLU?25BMY0I+Y^o=*Q1Qe>uV``F-XXu)j9a;m*w19oro z<(q}`@ets`!@2hJoj&gbj#ng3ER%Irl1Q&ReQ#=)^oE3QUl-3>-2TQjNE8rPvb_{U zu^CCDr_`#L)a)EPr`oJt|C7>b1pqQ^!7SzPDVUj5kID9h1dpad?Yun(DRZ2m`{IoC zNx9761|{F;AyJfqoyl+t&$1ajclFsjl-}MK{~Ti2){I-;Hvn)i*``M#$f!$Z^+dil zaac;iG-jODYtKVGYdfbEKkj#;qAl>&K<0g_5H92dCsPu^L4j4Zywyf@O`AI0U3 zlbVCch*$`qWhs6hxo}ywEGg7rq&=@zwcn4JZyzw8G;|NFhjxYxEt;nE{xEj+o?0lY za5P_K1(0}b6>zj75fa%qqqfp{p^BL}Q|EJzlNyMRCACc|;6#$Edx`m$e0#lfCtA`K zCtcz)=j`j3i?kaCZWBIBwS78_3hr|bx*u6pr^}{%;qH@S60ZoeHfm&zU&W$|9Un%x z6@=kHSJg7coSaa@Q596baymA(&~eOJ)V$BytUM;UB-T=fjMrnjd`)XS{901OvZqJM z^Gdzg>c3ihyT=tKCmuQK;6tH zJ9Eg4HEyp5^05VZybcn5&trtKzOz`{L?0=)_6HJk`uzQe1Irji?@Ys|_-WrLN;~0> zv~BKi2ZfH`cD2K+JyOJ&YPwe+vb!OHh;izAJ@BIKFJ;kVg5#>ZhLU!3`eZjg_LfTL z)|G{6KV9A=AcwQzc^QFC+mX_;6|9NNa1mFdaT1lKQO(Zel&AVBIFdPZas7>i9sFHd zf927OW$9K&+!j|?0P#Qvdi`8#~v zq#Uo06W7ns?HmpFv#zWuXoM0*K zqLIiv^)YH`PK9_yu zVo%VR)Y38GIymQp?$S|Rz~&qzbnqqfPSy#<(K*EL(y0DvYu}HW^p@~+-iVw1a_b}^ z?Cs9EkSD)#(TE4@j=i!|GEw1~2aP~B>J`FUGhs~CQsmDD<->ZC!gTX`GY?^TFe@oHE`cR5>9x%)qH0Ec``hX|{;!ggVfpNE`v zq`q>Kna1JCDo{F8I0RWotI`>P7E2^{lWVf7MfqetA^wy;)3hJLJ;B7(# zRp$@o_0%SXYI~X*3CMJ-g2PY^f-R9^kuel-J5j~R8ZHQM=27RmXSpncCXnH&t9p!q zq)8C3_mWl5fe#hbX;sGqUo#n1OWD&sHE>n)5A7&0UtKeSqim@H#PAy|8-u=(}pl$qV_^3MwA$_dUAbg;kZCl&{HC9XM@(0-uwS zfM7mGI3sfBm#Tm%mHK&swFPiOqoL?W);kQGRECy|ET8&=!5*&N19x0LbjS?f76T_W zb)d*!jXZS+CmCS~s7h*Z7y88fMTviJDbGJQj@U8v^$qju*cWX_87hZb6A8biVLgF= zEo^h|VQT;_)O^P;dTsmjoYc*hv~8=Xp&OF&af@t-8s|i;RcN_=2XE_GYnLV+b*X)m z7~ryOGH8W*=*{5T!jk1)>yoTa`%m{VKgC)qn?&UNU9_wZdTrT1{*Z1X@AJs9hReUm zcG^mLwX8-rHA)_x8VFb(T(bsCe~%9AYWFwPvH)WZHtc|Qjj6<}=;3+w6+w=}qI!pa z^z$4YoVdEg+Ijb!DG8rAfw899{}gLA!Bv387-|Qf-t!1qy>i8B0c&63Ou4AGdM<8? z30OC`1(iKQ%a$oJDFbS&6R}pUFtDyfOAWuh+KqUyD*8kLD76kS1^W8NLx0Knh24Us zyj>z+-jd$u7K@czxmn`pi&bG2fvDP+XMn1@<6&XdPfJd8x~}f(c6WQyqbI!9bAV%h zCCl`5fYP#>WcB`Cx{qgo{X$UlW7brgV+4ObU*~50$I(PiN*^}I{uMuk#6eJ*yXc*H z<=j|Qvj}jqIYzT3rJ_5sJ!G~1Fiy|kD|`da)}_5FdBqM~%BsOxbN>Dr2pBQ1*Mn-x z3jSTElV1QmxdTg)$7^a!Z;>YABA8w#Fs74S3soBHd!z}Q$g9eE1U4NY-Y38o;6p3J z{%P|IX)E-3J7A*OUQnvoFaC6nMgH7_lzprZ8oMaECNEygVw8UdfP-gJRn+FIJ?=?~ z9K}btJ?Q5+C8^Xe24bcQ?ki%!e6TCgo(_WxHFtjx)=zI^pM&dlOJ!Vx^+iw z=a&S|Qo+YBfQ^X8ULyW_V<|kspiQ|hZ^)!1I5`{$PAzjY)rVS>oPy4#SZuf{2k&Yg zyF8*Q`mFOY2)Gh15&xMwsj*P)5nGlY!i4 z*Q!qC!4@=4%l=(>gAujOoF$M;>l~hB`{`M17?xK-E#jY5#aQdsT4&Qcn&kR^{$$sg z^&h<9%`S`j~s>M^l;YdD`hWy%5B0cvnHFr8$`8${mwIT z5M2GM8_r9h zl{x%c;7QKsj$nHA`RZ+gYwf0rFCMgM?ldnt2e9UgudK-Mz(*VW$)sm#l_rih8lN<` zQ*{&Vy!Y6`V-5dSaD%2epZvQ9aX6K7qKSgjX@RbwCTs%^nYc2Mb&yA+n{+=yyIBmD z?R%7B7MzvU@T|T6I*~=<>qZ92AMEdsmM1O1SP@T?o0BC+VGlfh)787~ZT(BukCHpk z_Uvtc7UkQ}W23ZtiHJMB1e2vEK@3JFIgx zN95&tPxi~_`6(|FgdnQl@&Np2Ip2t^;cL1}mb%9ltBT)_V$H&1vM|A1U_ta^(^|4=d7m%Br%%1iruv^|0I#SR2Pp zPnIeE)(_l%nKO9+PpHTGWIyDczE#HTIpc1d7+3KFw9@S0u+}-z+AX9b3$ZtStc&Io zEq$lmRHocq;POiiDMU`;X7$IF8L?@%g9MtD=~g&g`Nl=ZUs0CrvxWYq+;@LrRW984 z#^Ny26mCH59pPf{G!+CKom{`n4;|hRL-g#rWyceJ!MZuU)##HNRc0TPY}5LN%~PofnacpjTPxzi>bH3l#OytL&AsK(n~k|r3r`E1tBTk zbdpdv$|k^|1}TltxFJGJ-}e-Z#M<(c*r9myt!o*NT2_ zdq)qtDG3xKx2!9x+aKRUuI2;Luln0}JDiV$$Eh2)YyI!8RVQe$*$R}+MBN<+&)`P& z%JbeuzR5^GzA)i_J>cLz}1bi*fKKwvP{Q5^6+?uE7s)ru? zPm=o7Ao2GhS`nJuD0~Gy*WVBM+sybZP2^eEjg_q$&VHYuQ2}VoLAt>9abbW#xHYdH z`NR`6k97WyN*|~berEwsmobG4Y`HaWTM?>iT(rjq?XGr1~6|maz7WGPH(oKKeKPg+qwTP?ISo6r9T7CHk%Fkk6N7#nDCQUuSG_FS6qh|V3GHZlOp$#b~v~8HiR@rB6 zoAbP2H3m$uz8q@=BDr>UFa;m1UDS?XU`ZouM*0lx1cs;AlDn#g>49}x8XzXtet>mf zYz^^X#5MHW`bkm}lbM;6{uj-yFXyw*aWDf&2rDvrDpLcIjGr8m1Ar?`0uU;|NR<^t zj`Z(9hN=oN20se&oRU?V`w#%gMGAr+SufB40Fo*n{P@3L*eQ>iVTn?Iu;izp9?2=P z_li%c;E?%k%icqukT-|izXPv1MsLWO9xI!Wlp`Ksh>Kwihv?P7a>v-ebz1v!UC&$c zyUYj*=KYRRC+$+lEeSt96wFs`5=%)t_H|9dO-f%69pgzPYy~8 zJvOa-xvwnG|AQ?Cj%Me)r9a$FvnN!hz|R1M@moRw;kX_2Z6#NkxI7^G zP_6~qaWmb>?uqrE?8jW@{!LROr3u@MyD|Gw3J*Z>V{x=dpm8O~|sQ-G=qgnQL z;KTNRs^GA(r&W-CYo;haUvoYXIOQtn;Maz3ykrLy_DMNtudr1RxsScAt|fj&h^y{` zXSSKoh8cywdT)S~2d&qjd8s!_Z6)5%1n?Zl^xpb_Gtl1POIBZj06fml+LjMjY7P5( zBOo*#th+`ald3jyaupi>jI!e^EBY_B`wRVA&+*4&Z0H+P2IYQYE-Ss&g;-KDt!9@^ zI?Lv{Ca;u{=2qkJ@isyCofn>3r&3@aLTD3Pr~rnPj}J$A%lOL5JGxID)^kGCBH8Gc z%F&p_|bJag>xu*sFKj6BZ@0%O(0=IQ`oJd(k{f4_tEmOlJtnSePb@D%2@O!Fy-707)X zI$Z7+xoNXgV4`ivoqAWPK*nWs}y76*hOO~zp zJBGy4?d^uhImUn3F-uY557B^f(71_9v){5bNynsA-ycLaSxg% zE~OcgzMUDmJ`3}gDmhWGFj$_ci)t8Gq&B+HdLHOT+CQ7d;!p>5cJ;)|E;#N!z2jW# z(ZNvix_|3o-wUP2SyJnPzmnUry);q488OzuPJR%#*^b*B=RP!*m=^G4%e@5-DG82Q zw+!(=L}YwXB|LJ*OwLsuoS*4fuJp*Bq-dxhen6C~Pnc4xEh$$?1S6?6Ugf6Hj-0x6 zMi3ImyrlvPyHl&+)v22pDNZ)|-tNtZI|p9jWT+1wUQyhnW9=-@4=Xq3_~UYTWL06n z%z5T1A7~`drJ@Q6ze7ao7>j)G6~%?**`)IMrXqcgmfG0zvJt1|+J~yPC;BBPN-xP<)VzYGu_%4i3KAX( z2()uVP^@Nw%;4y6fp`v2OL4{uKVM<%LNk z5Snl$VGGsqq{~tYlhxKO#tH)2K6}es^ene|+aG^B6X0!MP?IClpwxjF{q4tDRkI9& zcTc0yLPE^JiL20BHi=F;hf`kD_ya5$7j6GQTxHJem8>}xWBCPZAl!1cFrs9Zk3jgQ zM}DP&8kkUS*}+SE%y?80YPHAc8`WIDAiNeDV|T2BQvR52Yrhw;vA(@3`5HGe&Yt!i zYwyS(CYaV%tOR4q&AyxutmcKyK-&slYI5YwwfOkFX8Cpf4_m;te^hIJVyny3pvlW= zZ`?1mhwjtr)r-Pn^Q8_gRBBJY49Ovb$9HKuRoa$|q%tfC)H1fV`?ge}>a+Q_#Mzr} z4}LJ;$=FJ@!Ih7xgUq^SS!TQb23XynFdn{K?w02d?3aR+F@IIm98vWR7O+KrYv1Nh zxy?JSwva&KhhI)aRBI&+=t!Fu>^5!i!?8J*VVPSCtLe6ZZKpOnbPpLkdPLLd)w<)8 zT^xDIyRsx1`GOZQI=pyYOWO4Pm=VeqW-Ge3J0vIh%Nc!OTcH*Ktm`kb18cunf0kw> zBUxifIZp!ql6TW_OBKx0lLV0Y8oH$SS^!3ARIGsEhL>8CJ)PE8H#e_eoN^UaTUdSU zeQ{eh`7=g_^hUsD0xNAs)j(+WmAh4!$PYNo+{l~j*$CLaug=~|)R(p&gAex)hHfB( zjgeHj{X%iFwpoN3CUE#x5FX3xH?p@30FWm zKh_cCiwTICUUDQ$$DO5H8z~YU*cenSLl$+2%$%V;;pb`FL!br>tdEehlg^T`oxslK zXV6aB?_x7qxEhLS)#r%`ZYN1^TOAvJHP_B5zJx4Jd1@o(Ywh4A+3riN{z%rDK`EN#rA4p#)~&KQ+b$b#7kObAliAP00$Xp;nBW$rR6OGjP@WzaXs!=# zrMqL|PhEs|u)*q;(y7{jnR0dY2mtr-8I5Ysb7<#n+r$xDy45UFiIYE8B9^3Sej36- z3Tyls>FgAww*m^=S%%sc?>pCWO6nKUWsC|!KuRMjzSE+g80_MQRjK)YG?WvZEcmC6 z`hoEj70;G2RlSoWsj-3j)B$iwo+&4lKGPJ?Lkn(A_JVT?&?Do9t|AphKu~a2M|HcN z`fMy|^vJBE1XCO-N{ox#c$3GQ>n?GQ6X>o54?%82I!NbFEWeVg&oF@4fE}%-_ushy zxUz5#g5`I7>(E7`I%+jq02fU)Bruu`#DqbSyp-0@LV1^R-CF}Y1qFduQ<;tGN>{-P zQi#f}XAZbQY_~n;_GedqO>#0W^0Zvye;Sbdu1eSKuF&~*;S>m~Jels$vC%6I|2qRI z$ORZ29TYmHW|%&FS_wSG|8qo=)d6>0>=4ALE(?s{!yiDd{F9rE^sKt%(1tk`6*zx^ zPn#+RdN;wi0Miv38VA-_19Vd;&~ThCdG~LIk#Ut$DJQ0#d?p>ww~)q0z`@t$@0`Lr zckfLnA=i4GrWYD|(&HSbRT$fOVN_-@|7nB)6x|&R#0^tSw@E1J;yR5agowfbo0=?jQ? zU041ACjynVQ*dBqMG9lf&s<4F6t}J+)HGF-ZxSD`o>R>>2FjD7SR7`G-=S4>>z|Mp zQCv(})R&1xwwRyV$Wuu#I)FwF&jtIimlmJ9E>A7ISRA6}3gBWvZvRTBGi64p!pHop zMz}lFk3TW^lJN|(EgvY%tv=(lZ~Rr=%QW(kFqxFBFF&yFA=CQc zQy#BgxSg!qaNHbVDjmwiARsg+SSA2t*feiQ@1a};m$K91|1L_2%m-J$dN$|VF6>rV zZsidCA0sP2P#SyF9{LRwNzD8#>Y&S_E~N$U!Ph_t1;?sjwPp0RM_qJOy)=X?8M7b- zQH7wT1M8r&InJh>c*qf$73DCHIu~}^MtzyE76D;AV~h7KOYbv3-+}k}g$oUW<-;|U zcT-HIGXD0W^DG}Uy|=j;IjKqoKs7L=lROYu$`{G>Vy=2>_a7EtyaMEQ&UIf+t!mgv zdG?n(mkLziz8G=gqoN+!Fzw$`P~6B(?JRH|SJgBVm!Jcik_8Ka4wMl~pS85++5A0F zRcz}0dG0%h&L99!O=FubNmaTgPiy1@8W0xT_+*xxrV&n_v|~N5t3>$DQ+$V}K6=b3 zbAghI1B;yQjmgEcqJ#U`1)uchX{PS-?-21zZ|mn(y$ zVi0$=iAB!Mr=v@mm*Qnwp8w`_34FZDq2cJlKJ~V~I${7aCQaXYPRj0Jp?o`16BEHs z`ElwyTX&78Z5-ZiA+LRkVz*oKwtO>S%qmW4^m1C@z9K>2De3Nu1y3PfI}|m0x^8bM z&toW$$8gU?leq0AdCdas zbouvIR}dkwx|ECZx&?lF8wKp!@^gRg(q+*+J;RE;C#8gw^)jtl0a@4fNC(TWk7ves zTYN3#f`9p_{HkHhtG#_KRhg6X(}p@2jtCY6!lc4-I@~gP=AL82VWGCsmQFC`IY#uf3dM2{H85j|qt!(?^JG{o$)rz6gx=5oMmPmQcka`lt*s-aC zdco2es5)1Z?6t+OpgvM{%Fa{K%4W^QF;X;6xw|yrzA(7`x}yzTZuv&HfQB{UTF!VG z>!w{({*XjIaZ_ud$!dKx17_-AddL|0zkr??G-BJq)5rshDXREMP+Z608s&3w)1L0(4;|GMPkl_a*Z9|lv}ppW zS$zflnM;=P;e@agTU)_Sz5~0GJAAnYz@Bs?O8YFS_&>A6%Oo~c+B`4P2#&(4&}9h( z*{Iwxj1>&?^YxS+5RQnYX1({D9_jPW`u45bIK2KASaF`^lXOcI&1V*)OZE9)6(?}Y zB)(18A!t=mZfMba>(1vlP(Z_wzi{^+WKlf4M(kuIpZ(VVgdNw^~z`eg-*n-@^K2@^%eH%hO;sFH)g) zn&}znoM?-BawoZU;d=kRv9@jA-k4EInpiD1t-68HYk`sTtYEnsE3okC@k@Svg8PGB zvh}#k-$qf8MUPlg85mh`LuYF@EbJS+O^zKM1u=I^FcV*Fogz8K!OR4B&5CRW=~+v& z*Bh_QZ*&Q&s^Ebl9q-gq=qPoiX*m(m;i-$9|Cn7Ba5XyW;uQ2eck~Qq-VGO|9V`gT zW>9phNz6ACRUTIUe3S~_H3U_`>%IaJ}dkKYTUa^f6LVsBh4@Yx0KUdJB@%9=etib}rW!V~2c!&9cAek@)(g zE=)PPPVTqQN6=!91Jm9P$jv z;mf%|W6A9y3z?M-&|qF9Bk{@_l9f4}Khfej3s#Rt*)lfGQC@UeD{eMHX+#knOEKla zs@R~{vD6Mb|2gZPoi1OF>{|N!pX#@DkRmZToF-1GqM@A=^;}-d+$ngcRYg>N#+qkK z`IcAU{vZ3-Ow(v-pR1d-(e?7`dDV4^W9g z**v3JmlZvu`1P83YK^qADs5q}nqpD-5{8^*X8w%F8yK?mw-vyM7qremv)O`IL8MpM zRP^l`W8Ir;?pvA9^>|O|F6cDNWnH?>YS;FO61`i(UJLNd{>9Ph^%&cU`f-*vTbyWk z5XD^^A85UBKmp?nssNk3Y9+x{Lg~J9j@YPPlU?CF3qHQExlhY)PrekvAWshLKWIm;^)DVV-ts!=q>5o=R(SMdu5)V(< zf-s_Pe@yK;f=g~tq>l&vQsK8pCnQ=Getc+J`M=@sfw|U;G1e*|x7`RRUp#Li0c$uY zX_v$^sgqrka*ERIpIVBy-(GsxqzO?i5c$pq7~LfXRwm>7Ssyg(5_g&v-}nMp1{&b0 zDqB~ihB2nGD-u-%hBU9G0oe24*G3?cR@Pp))^#Ch`m%0&q6HT~P=f&1J~UO9AAc@B zXj?e13Z9pMm`&US#@P-n!*e|M*iyV%XK|u})NKl>1mCqc?1kMMVGj+Q%||CPC+V$khK}9&$(gqYDHWp! zL3G$9@}SxZaDnL*)cx-xYO7U1oxncPuEy4%Li~(kN*Nm=(tewRe{5*`N{oA^5OeG2 zex^_uw@`SFdcoGoU*)$%Cm4u~yN(_e9{&g4mXor|mv5zo+h0AI>weN_>|=QlW=1}& zp0P=u<@z1cjhEAGRI0jVLTDMjvGy1msOhxiu{j?{etwEGGpK%n*|S4QoM4RhzKIY! zrF&_c{R5R`6{aj!8`3>_r~xTmm8*a=&^WXXTBWR&m+noo&(zD!eN!x7_WMjTBunr? z-8M84f^h+Wi3Q3d+mQT?Kd1h&+Nz|90X?#K(=!!jvwG!}+S+zO6Um?CVN61_)&QA1 zl`H?gN=k*>zbU)FxfCZbo)9xiX2F8X`P}~|vSP^f9O)?VL18dR)}4-n3IyKgf7{H4 zs&Fq4%UlYl;TgU<5|8EvkUz(hp8Gvoy#Ou-x20jAF5P>?j%HKA#3kA{1|W_0dXq!+ zGXgvtIl(x^HLKkw30fLe<_IS6kysOmr&A-So@uUv^~O*_bjpVyyEZ1sYd19tgUQq~ zpA=BaobvZ~sT_8!k8%rp!K*cp%Reho=OclTghl@XgXFNzL|y0{hdh`(ZYAYrivOA_ z#+;?zqYe2~@P|u@f)3JsBvMZedP0fy_SOMOectQzT)@P;^vk4Eov`m=Cmc6``bb$s z<_!Or0W`5b`xn!T)7bQ`a4c@U=lWKJtUwHidV}cN%qPwx+KlW?(ir)sLpmtco}6w- zQIYIxdxf@z0B3bAKCT&41nyh>WuImWGfwR9(ZoCm`)leg2=9HRwD{q5&+vX!Q=gol z$p2SB{r?ar5@c0@W?zw4=ei9-ZJ=A?l(si>Jr9t3Z|91$OvC8fgvZb3h)lC;_qPR& zB}#2QBP=Ft+%m+Q%AVx1LJy~lmpaPbmK!QVHKy^0%i$litDjB2kWw-nsl}S$1j<73 z^xDB{BR+jwzNsoa4Lh3NhZ}Md?F-(S=2gzi7X$%SMgmILF1azsYr9KJ-Ux->f7iPm zo#grHKx2V9>z1yv>wZO|mcO3-gQ{12O@*4a?Q%86j0ceu+^+}Dp$)Ct?BPL#LakuS z8X~(i>oj?Jp6H5e51Af{*blbgH^9e)9W82fwuVJgU3h#N^P@ zQ0=n>1w;QzRYnpl=%eiD)%Ato(BqBd#*Or+CcSX@5&0zPy3Rb#!BD0n zDD2djHr9E14Ijasp2%B{X#45U>@i2o!wWIWq#Tv~x(=;ly(GTtn;yN0J5{Y+mEi%X zq`?HdA*whUmuWuBb)+1afj5uJuh^`DE^rvmx8?ynlh^WqquGGOP@Tz1zMSD4k3j{+ zt%J4+Il^hGN{ATFxqs!SX`3sq64e3!cMW+#!8}21%MW-*2Tk(Kzk6>61kbnOvX3-EFq-|h-3Qv{xDJSo3q_4<8NGL6 zl)S=yS$N_E^|c{x_0Wo9(`s%@$%bzFCkD9Vyn(inR%Yo z5>eP5}(_-B&#Ioa&rKmkgZNp^Bmm`lEGUbnLoE1_Qv=DTJrx>hQSzIqssE%|QR zBT(Hc@4Mh$)xpfs#YZVA^rToTZ~yVPC4WhF1AW7U(0JJ^GC!vgUv=RItu$dlAaYxz zW9d&t=mSM4EZhCG8)$ZB(K;o2+>7qYAaBE4Y^rc_|n%&T_=MqJ;%0H=E&f$IE z1QBC7_gfm1vjBz!$bVk-8Y=JVYgdd@Vqb;6;|sehBs;gGX-7h+MU*|rwhPVu{ef_6 zk>g3T?4%{#E-0r%Uwtx)FVUr#l`gDj$4W-Q7%!$RL6_ysC$7RTv>!Sd*LI{o*Hw8} zSO^o;Z;oa&scgHQu)PiM)o^{yWFkZl#0x1%zPYzve1<`ftlW<7FubOspoO~|+p}XA z@Mcz4EB%7};1!Pe`+=5xb2rwk^1em?bKk$EJ{0>Sm8s!Pf+ZLuQr0^}gNQ_<;((dc ziBHIoJhHn2D^LSpIw-;t6RQ#k@Y5hc{`$@%nY~0_33BG(yN#Q&G(h*nx3UtLW|~wRX^`%>E}9RR|!3#q|p@r?qS$J^2yn z%S9)(BAM8TMDfJ695pZ#ZZrbLYw?kZ(-9;Ws&8)^DhrhfjNh{z4Kq=$ryi02?Q>;G zYstfutJVkG9x25x+JZc(9Uksz@Qt7R8mQ_#JH_83Yhbup%!TT!&kn zHETHT$0)Qz3WW921aoo-St;3#l-J3DL~G3!vN>o z1YK(|(G30P?wnU5l{PBUgRku_jEUjy?MQjc*G#uKz;hKfTNme@vc)s#f2niW6R!cf z?m*mOOXXNVzE`xu2zSdBQj+dD#0rP$)6@WaK4jE4p~Yy=~7y zx^7bULQvZrc&l-e47|}e+p}#MiJ06g#d0leQ&$~nK-|0kG=59p@PYE|@lxLLQvM3( z)EwIu|Ki?!b+HjyteZ^;vZtsYD8PuL}QX%ZfXY*268$sp- zDUhD-Qggi$O$D5Zhto}E8T@O!viy|R4Q2QbWB&1zl~GxF|2FgjzS`$pRslB+aE@;W zibQvWC*4#K07;&H{_5t?)nm}LkfN~)o_=oy&Tdz2fA91ZKzMzY5+D`*Sm*J{F4shCLAOn5E(@tOAPBt)ai;~x&bz7 z(;W(Rn4Gep3iVbT=Zz(8X!Es+z|NZebZUCu4>n`K@bS#^2Vk~P0SG4*2>#d{n$u0P zcOC>~!hx_=fCW_Q<)}p_Ov&PXxR)iBR2IqB`eOty#Z&R2opBH0TB{ep zFr6!Sy{#94tSHv8bS5B9bY|fj_F2LLkLbFW#a{Lo^@k6XpGfDBepsJK zIm;`8>F-_GYKAQE438ZH;WXCtOjx>MwD*1~A<*i90O|8)j$EQ0n>=-SV#FkfL9tUH z{bBRDcw2wa2^Z9X$#kYXdbUQBC!H%FNh0J)8v)47UKo@ojDs*av8P50BKbhoRR++d znVgzg;@M(J>K|CZ^l3IyT>$_DJQ>naWzq!%NaN|f`XjH0uy&K#tXqvX7c5^~eFp2K(799p+L>}eZyD9BwgM)5zhweFd3^|E<4EX#o_$h0 z;(J+6gV+M+o<)C}E4}y6OT(L`k9JQdF?Q=ztPRU6XFNaKZW30`RNu?;ga7AIQ#*@M zfj(n(D4#_lyqUaNnMGA=OKU{~3``cLmw?dGZXX?gxKMT<>Vl zqvkF>j0$DhQ5d{b0E~$QG5`DU>@WXG{r$+St6}8}NVj>XhU3}i?r#TQ&^KaUor-Sz zA`Q@aH1-s?BRe-vVpMS*C(Aq+-_+1nS~#BtG5=f4PrP@k<=oiu=U|!s3{Pn&k7A22 zq569ya=&Zev?T=1t=4rap&oF{Tkn_2ArbZWSfD)YHVaIgkjqjaD}M>*_NX5yAa26Azs@a3<)?UHZEL{S62ZS7Fw#D zUD-PWa*(|HY;!zi9P+dWO_Ru0MjtPz{HLKSf&G~t$<(# zi{xBye}gHNdl%UQ9VXr(04{ZddJQvky5TJHT@IkDMAT%F0vfiq?+$}E%lcc}tKf?f zq3?geBx*U0bfL9x{)oMRCw)L6=s2GUg1eb?njyJG{_sf49&;t=q3dL1Pw^Q&|HU~- z-4j`HLTur6Fh%@AmB&_g-12s1I@rFZ;=uE1gzE~1){sn%z?p#&8SraH!BQAN`;3gue z)#k|cbUf74hU5@&Vw{r$^fX*_0fhe-INOO*y~{H9Y5MlZi$hZF+2y;mz?L1-49MQr zo?W}WWZtV^IG-Q9dOQFTXgMXmt%-y>OaA0eNG0z#R!L_#Z1a zvOpI7iPMPsR57UUuG;H#Sw+ZZ-l%!kg;)f)eD_nZeIBmrjCI?q24q`DFd0>Q83P#z zRGr`tg|E3ZJpxn2ugOy0&9z}O^87a$!S$C1gYLkAIklQS!3to;YWJfxUEQj9()y4b z{kOX2we6+J*Ri+QKr{b&y4D3*y9R*c{v6{Qv9%stQWG4cDvN z(u8ylLf0B6fm!A$^w$O$Eu9OhWr>HL5_I7x=lY#h@I2MMbZqDS*6o z1pNhrtM9rR_FN?fh z5h$deq1(;c?G<|ydi)fSqHBi!B(waw>P5>Jm^f%FEA;B~a0!0t$Ch}OxSQpwXn@{`A-v4J8KJ-B{~ii}SSh)ajzL0yTfscYTp*V>{UO0rb~9ry$oVW!bii zGGTCZ39qh;)^PyX%4(jV_WUfq-#OsqA6?KU)xp#Pk^ba=#yh|2ld0*XL?Qe+Tdvi2 z6}Nmp3lK+YDia7BVi`ftZ}Umdap=FZ^EnK~6Tz1P*}+Z7XvEP~_@u)%$Xc@=oc*{e z-KViZx_2xRS!uWrSKfxXt@&*BM#UwNLbd1e_OK5d`czyNHs#Rm?O4OSZ-wC2;?!N( zP^9>WEyDz(L}|MUd*VbnK8IuS9&EduN4p!roe7>}- zBhZLbU#axdS)BDE!nk~V!(&Mi>mm@h_J5JAu*3BgSZaH7qml_eIJF(jlNS{CiqRSN z(6LUDsNwEnw0aAwF2yD;8gT*hga~U_8azdvP@XqifZ=b~2={4A_LEQe*$#N3Q!ih@ z+W}K`1#G%~@WBnusmW9lAMArDs5}O^Q$w8pyID|Ye3e^(?ISIhmcbsbW#~#YTz8cr(X*b4JPB`8u&xq!;_ZPxJPiam&OTOJoM95w6h?q4 zwWZl%Wm}(Di=nl%C`3HIVhAp^8}+>)=>EdP!2G$UQc021^e**!cl4m`Bg?U_x`DV z!({3Kw&OTlIB=)Q`}!Vr-_Dn?uXb1gF)*r;g}P(8TWSp+P1l!s&7*#LXD(RHe8Jl1 zn-HEQf-gB3*7f1-jm_l0z$Qn`105Z+u?@1Pje&{96SD+naqEg zLBbC@x>Cu;^HlNCRm_b!@s{sQtBUA9jWb@C#@>ZF6J8A$|JZ?zYL{1gy~u&SkBw?? z$5Ytcnh*e1j`M!uhX2{8YoS&@vvO!G9fSnz?n;@{}*6| zc=%r^W}Gy?FQhps;oWo7dj3c#Ro_6qnGOBL2)8^h!4%YWZDD@TL%})F_X}ma_kHkp z6;PK=6HjS?{Z~{*a)>Ssi31b2g5J0RQ-~y?-(n){5HOVVLYg|25~@vrr~hO>I_oE=Ee%>;RrRt&mzZMi5XYSw;bq~Y7r&}0G1&bZlM zD3Njw%vwZ_O;`4EiEnL&S71eCNPj{tdZet~HDTNy4H75%j5!V4pTMl%?$_Y!_(JyQ$mGwNM3#?Lf=XV0ieka!@6qp zuPSS*gL?%a<=P;Ixt122fWLqC8|9XcXYpKn>2Kv4S#Z4pRQ0DSbvxM6hNz@v;;zC? zg2J_vL}CqiP6NiG=bAUIQ75{=yvJ0S*3Fi+7)y?St7uSM53i+ebM4&apJVZ|YrYYq zr*WE)JeKss*F?%%e%n{^k$26b_8*_}jKBfNxHgD3VTwY^!&^7lTKxY~3e(EKT?s%+ zvQ$ec~tQ8^-a^|U09-tvU0y!nVa$g@i< z>T9Lmn+cu{MY6tWYtk4U_iTYkOPjW~VT_)4x3@Qs;=LWugWvcs9 z?K8OkLj$56-)*@$yTJnYYPxhsTjF2@qJF(bh@D_*uTxO-bjaobRC=o(ia5tw`+N7X zYPW1dgflr>nES*i-PlNrSy%o`bHvQIGJdybAi6I^q~dqTu>px#1!kkN&6$!F*+9ox z%X2BOqo=?VL${3V+~bjoir|mLEw`0>pBpy=6D`ew*`t$+l({=Tse-#{j^}|UHYhp( z6fm~l!lYOA`y|W%$5lZu0F>lf$qoS z93NttE_XHFSG;AG?Dd=4%86=34WhcOF$aciI*MLKgo*{vhkK>bsO8=7AM{KXel*Y_ z7u@PGo7zNE^8hSp#c}vBXr;yG5pP*YQcGaVmkCttC9i?Z;ceRJw9|`fKf*u1_tepi z=fvwUi!JkB)_>gVE5ZEaAqZ0Es2p_K;B*7oy)-}0()oqonsaq>QeizQwVUrZk5sA^ z!6YkhZ)qsv@ecdRMeyBgndGY4LGe(@XZJz7_@Deinw=oNiL^pnsm*Sm_M@#~aKB2; H;pP7UA`ng! literal 0 HcmV?d00001 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8b668cc..7522a36 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,17 +1,30 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { Suspense, lazy } from 'react'; import { AuthProvider } from './hooks/useAuth'; import { ErrorBoundary } from './components/ErrorBoundary'; import { Navbar } from './components/Navbar/Navbar'; import { Footer } from './components/Footer/Footer'; import { ProtectedRoute } from './components/ProtectedRoute'; import { LandingPage } from './pages/LandingPage'; -import { AdminPage } from './pages/AdminPage'; -import { VideosAdminPage } from './pages/VideosAdminPage'; -import { SpeechSoundsAdminPage } from './pages/SpeechSoundsAdminPage'; -import { LoginPage } from './pages/LoginPage'; import { APPS } from './config/apps'; import './globals.css'; +// Lazy load admin and login pages +const AdminPage = lazy(() => import('./pages/AdminPage').then(module => ({ default: module.AdminPage }))); +const VideosAdminPage = lazy(() => import('./pages/VideosAdminPage').then(module => ({ default: module.VideosAdminPage }))); +const SpeechSoundsAdminPage = lazy(() => import('./pages/SpeechSoundsAdminPage').then(module => ({ default: module.SpeechSoundsAdminPage }))); +const LoginPage = lazy(() => import('./pages/LoginPage').then(module => ({ default: module.LoginPage }))); + +// Loading fallback component +const PageLoader = () => ( +
+
+
+

Loading...

+
+
+); + function App() { return ( @@ -20,43 +33,49 @@ function App() {
- - } /> - {/* Dynamically generate routes for enabled apps */} - {APPS.filter(app => !app.disabled).map(app => ( - } + }> + + } /> + {/* Dynamically generate routes for enabled apps */} + {APPS.filter(app => !app.disabled).map(app => ( + }> + + + } + /> + ))} + {/* Keep non-app routes separate */} + } /> + + + + } + /> + + + + } + /> + + + + } /> - ))} - {/* Keep non-app routes separate */} - } /> - - - - } - /> - - - - } - /> - - - - } - /> - + +
diff --git a/frontend/src/components/Navbar/Navbar.tsx b/frontend/src/components/Navbar/Navbar.tsx index 58a2dac..68875e3 100644 --- a/frontend/src/components/Navbar/Navbar.tsx +++ b/frontend/src/components/Navbar/Navbar.tsx @@ -72,17 +72,25 @@ export function Navbar() {
- Rainbow + Rainbow

Rainbows, Cupcakes & Unicorns

- Cupcake + Cupcake
diff --git a/frontend/src/config/apps.ts b/frontend/src/config/apps.ts index b42e3f2..8930fd7 100644 --- a/frontend/src/config/apps.ts +++ b/frontend/src/config/apps.ts @@ -1,7 +1,9 @@ -import React from 'react'; -import { VideoApp } from '../pages/VideoApp'; -import { SpeechSoundsApp } from '../pages/SpeechSoundsApp'; -import { TicTacToeApp } from '../pages/TicTacToeApp'; +import React, { lazy } from 'react'; + +// Lazy load game pages for code splitting +const VideoApp = lazy(() => import('../pages/VideoApp').then(module => ({ default: module.VideoApp }))); +const SpeechSoundsApp = lazy(() => import('../pages/SpeechSoundsApp').then(module => ({ default: module.SpeechSoundsApp }))); +const TicTacToeApp = lazy(() => import('../pages/TicTacToeApp').then(module => ({ default: module.TicTacToeApp }))); export type App = { id: string; diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx index ffb18db..15ac610 100644 --- a/frontend/src/pages/LandingPage.tsx +++ b/frontend/src/pages/LandingPage.tsx @@ -28,6 +28,7 @@ export function LandingPage() {
+ {/* First card is likely LCP element - prioritize it */}
{APPS.map(app => { const color = categoryColors[app.id] || 'pink'; @@ -52,12 +53,30 @@ export function LandingPage() { src="/video-marketing.png" alt="Video App" className="w-20 h-20 object-contain" + width="80" + height="80" + loading="eager" + fetchPriority={app.id === 'videos' ? 'high' : 'auto'} /> ) : app.id === 'speechsounds' ? ( Speech Sounds + ) : app.id === 'tictactoe' ? ( + Tic Tac Toe ) : ( {emoji} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 1f97208..f45dfb6 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -5,6 +5,22 @@ export default defineConfig({ plugins: [react()], server: { port: 5173 + }, + build: { + rollupOptions: { + output: { + manualChunks: { + 'react-vendor': ['react', 'react-dom', 'react-router-dom'], + 'ui-vendor': ['axios'] + } + } + }, + chunkSizeWarningLimit: 1000, + cssCodeSplit: true, + minify: 'esbuild' + }, + optimizeDeps: { + include: ['react', 'react-dom', 'react-router-dom'] } })