[{"data":1,"prerenderedAt":502},["ShallowReactive",2],{"nav-pages":3,"footer-socials":160,"page-projects":183,"$wAqIHDRXVI":204},[4,127],{"id":5,"title":6,"body":7,"crumbs":115,"description":119,"extension":120,"icon":117,"meta":121,"navigation":122,"path":123,"seo":124,"stem":125,"__hash__":126},"pages\u002Fpages\u002F1.index.md","Home",{"type":8,"value":9,"toc":109},"minimark",[10,15,19,24,27,61,64,96,99,103],[11,12,14],"h1",{"id":13},"my-portfolio","My Portfolio",[16,17,18],"p",{},"Hi there, welcome to my portfolio website.",[20,21,23],"h2",{"id":22},"who-am-i","Who am I?",[16,25,26],{},"I'm George, a software engineer with an interest in self-hosting, programming, and hardware.",[16,28,29,30,37,38,43,44,49,50,55,56,60],{},"I studied BSC Computer Science at ",[31,32,36],"a",{"href":33,"rel":34},"https:\u002F\u002Fwww.cardiff.ac.uk\u002F",[35],"nofollow","Cardiff University",", the projects I did during my time there can be found on my ",[31,39,42],{"href":40,"rel":41},"https:\u002F\u002Fcode.ggrainger.uk\u002Fggrainger?tab=repositories",[35],"Forgejo"," or ",[31,45,48],{"href":46,"rel":47},"https:\u002F\u002Fgithub.com\u002Frandomman552",[35],"GitHub"," though I have migrated most of these to my self-hosted Forgejo instance. For my dissertation I created a website to show sensor information in Cardiff University's new Abacws building, which you can see the code for ",[31,51,54],{"href":52,"rel":53},"https:\u002F\u002Fcode.ggrainger.uk\u002Fggrainger\u002FAbacws-Data-Vis",[35],"here"," or my deployment of ",[31,57,54],{"href":58,"rel":59},"https:\u002F\u002Fabacws.ggrainger.uk\u002F",[35],".",[16,62,63],{},"In my spare time I maintain a Linux server in my house, on which I self-host a couple of things including:",[65,66,67,76,88],"ul",{},[68,69,70,71],"li",{},"My own personal cloud storage using ",[31,72,75],{"href":73,"rel":74},"https:\u002F\u002Fnextcloud.com\u002F",[35],"NextCloud",[68,77,78,79,83,84],{},"My ",[31,80,82],{"href":40,"rel":81},[35],"code"," using ",[31,85,42],{"href":86,"rel":87},"https:\u002F\u002Fforgejo.org\u002F",[35],[68,89,90,95],{},[31,91,94],{"href":92,"rel":93},"https:\u002F\u002Fheadscale.net\u002Fstable\u002F",[35],"HeadScale"," for easy, self-hosted VPN access",[16,97,98],{},"In my spare time, I also write some code for Arduino's here and there, the connection between software and hardware is always fun to play with. I work for a company based in North Yorkshire that makes robotic automation systems, so I've got to work with some really cool hardware.",[20,100,102],{"id":101},"what-is-this-site-for","What is this site for?",[16,104,105,106,60],{},"I wanted a place to write about interesting projects I've worked on, here it is. You can check them out ",[31,107,54],{"href":108},"\u002Fprojects",{"title":110,"searchDepth":111,"depth":111,"links":112},"",2,[113,114],{"id":22,"depth":111,"text":23},{"id":101,"depth":111,"text":102},[116],{"name":6,"icon":117,"href":118},"material-symbols:home-rounded","\u002F","Welcome to George's portfolio website","md",{},true,"\u002Fpages",{"title":6,"description":119},"pages\u002F1.index","iJZaQtML6AUZWO7oOxOQo8ckoA7ZFdLQwhAci4MVMik",{"id":128,"title":129,"body":130,"crumbs":150,"description":154,"extension":120,"icon":153,"meta":155,"navigation":122,"path":156,"seo":157,"stem":158,"__hash__":159},"pages\u002Fpages\u002F2.projects.md","Projects",{"type":8,"value":131,"toc":148},[132,135,138,145],[11,133,129],{"id":134},"projects",[16,136,137],{},"My recent projects can be found below.",[16,139,140,141,144],{},"Can't find anything that interests you? Check out my ",[31,142,42],{"href":40,"rel":143},[35]," for some other things I've made that didn't make the cut (or that I haven't finished).",[146,147],"card-project-grid",{},{"title":110,"searchDepth":111,"depth":111,"links":149},[],[151,152],{"name":6,"icon":117,"href":118},{"name":129,"icon":153,"href":108},"material-symbols:deployed-code","View my projects here",{},"\u002Fpages\u002Fprojects",{"title":129,"description":154},"pages\u002F2.projects","ZvgQ8iafkO8Vo_2oMkyd_n-BCCp-tAooB16GjR_rLf4",[161,169,175],{"id":162,"extension":163,"icon":164,"link":165,"meta":166,"name":42,"stem":167,"__hash__":168},"socials\u002Fsocials\u002Fforgejo.yml","yml","devicon-plain:forgejo","https:\u002F\u002Fcode.ggrainger.uk\u002Fggrainger",{},"socials\u002Fforgejo","eLo2tedWGBfxJY_VmYmV0uFkIDhRuQvm4fmj_5QD7f0",{"id":170,"extension":163,"icon":171,"link":46,"meta":172,"name":48,"stem":173,"__hash__":174},"socials\u002Fsocials\u002Fgithub.yml","mdi:github-box",{},"socials\u002Fgithub","umA4vGSgNhkainXfqvtesFnwEcchjMuYOaVvJl409do",{"id":176,"extension":163,"icon":177,"link":178,"meta":179,"name":180,"stem":181,"__hash__":182},"socials\u002Fsocials\u002Flinkedin.yml","mdi:linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fgeorge-grainger552\u002F",{},"LinkedIn","socials\u002Flinkedin","bTHOxI-WAMN6H2Ub1ipKl89w3nM34aSFOrB4ziV6Nhc",{"id":128,"title":129,"body":184,"crumbs":199,"description":154,"extension":120,"icon":153,"meta":202,"navigation":122,"path":156,"seo":203,"stem":158,"__hash__":159},{"type":8,"value":185,"toc":197},[186,188,190,195],[11,187,129],{"id":134},[16,189,137],{},[16,191,140,192,144],{},[31,193,42],{"href":40,"rel":194},[35],[146,196],{},{"title":110,"searchDepth":111,"depth":111,"links":198},[],[200,201],{"name":6,"icon":117,"href":118},{"name":129,"icon":153,"href":108},{},{"title":129,"description":154},[205],{"id":206,"title":207,"body":208,"coverImage":492,"description":493,"extension":120,"liveDemo":118,"meta":494,"navigation":122,"ogImage":495,"path":496,"publishedAt":497,"seo":498,"sourceCode":499,"stem":500,"__hash__":501},"projects\u002Fprojects\u002Fportfolio-website.md","Portfolio Website",{"type":8,"value":209,"toc":481},[210,214,217,220,234,238,253,256,315,319,324,327,330,338,343,378,382,385,424,428,437,446,466,470],[20,211,213],{"id":212},"project-overview","Project Overview",[16,215,216],{},"In this project, I aimed to create a simple portfolio website (this one) for me to showcase my work.",[16,218,219],{},"I had a few requirements in mind for this:",[65,221,222,225,228,231],{},[68,223,224],{},"Minimalistic and responsive design",[68,226,227],{},"Statically hosted",[68,229,230],{},"Simple styling",[68,232,233],{},"Dark mode (the most important feature)",[20,235,237],{"id":236},"architecture","Architecture",[16,239,240,241,246,247,252],{},"To start with, I needed to decide what I was going to use to build this site.\nI've worked with ",[31,242,245],{"href":243,"rel":244},"https:\u002F\u002Fnuxt.com\u002F",[35],"Nuxt.js"," and used ",[31,248,251],{"href":249,"rel":250},"https:\u002F\u002Fstrapi.io\u002F",[35],"Strapi"," as a backend CMS for a previous project,\nbut I found Strapi to be a heavy and unweildy product that doesn't do anything particularly hard.\nAs I'm going to be the only person making edits to the site, I can use something that is more technical and simpler.",[16,254,255],{},"I decided on:",[65,257,258,270,283,296],{},[68,259,260,264,265],{},[31,261,263],{"href":243,"rel":262},[35],"Nuxt"," for the framework.\n",[65,266,267],{},[68,268,269],{},"Full stack framework for building SSR and SSG applications.",[68,271,272,277,278],{},[31,273,276],{"href":274,"rel":275},"https:\u002F\u002Ftailwindcss.com\u002F",[35],"TailwindCSS"," for styling.\n",[65,279,280],{},[68,281,282],{},"Utility based CSS framework.",[68,284,285,290,291],{},[31,286,289],{"href":287,"rel":288},"https:\u002F\u002Fpages.cloudflare.com\u002F",[35],"Cloudflare Pages"," for hosting.\n",[65,292,293],{},[68,294,295],{},"Free hosting for static sites, with a global CDN.",[68,297,298,303,304],{},[31,299,302],{"href":300,"rel":301},"https:\u002F\u002Fcontent.nuxt.com\u002F",[35],"Nuxt Content"," for content management.\n",[65,305,306,309,312],{},[68,307,308],{},"Much simpler than strapi (opinion warning).",[68,310,311],{},"Git based CMS, data is stored alongside code.",[68,313,314],{},"Markdown based.",[20,316,318],{"id":317},"project-structure","Project Structure",[320,321,323],"h3",{"id":322},"styling","Styling",[16,325,326],{},"Styling is done using TailwindCSS, tailwind provides a series of extensible utility classes that can be applied to elements to style them.\nThis differs from a more traditional approach of writing CSS in a separate file and applying classes to specific elements.\nI find this approach easier to tweak, but it isn't without its drawbacks.",[16,328,329],{},"The main drawback is that it can lead to very long class names on elements, which can make it hard to figure out exactly what an element is going to look like.\nI find this is mostly mitigated by using a component based approach, where elements are broken down into vue components and styled internally.",[16,331,332,333,337],{},"I designed a few components to re-use throughout the site, this can be found on the ",[31,334,336],{"href":335},"\u002Fdev","dev"," pages deployed with this site.",[339,340,342],"h4",{"id":341},"dark-mode","Dark Mode",[16,344,345,346,349,350,355,356,361,362,365,366,368,369,372,373,60],{},"Dark mode is natively supported by TailwindCSS, and can be enabled by simply adding a ",[82,347,348],{},"dark"," class to the body of the page.\nI used the library ",[31,351,354],{"href":352,"rel":353},"https:\u002F\u002Fvueuse.org\u002F",[35],"vueuse"," to check the user's preferred color scheme and allow switching between light and dark mode.\nThey have a handy ",[31,357,360],{"href":358,"rel":359},"https:\u002F\u002Fvueuse.org\u002Fcore\u002FuseColorMode\u002F",[35],"utility"," called ",[82,363,364],{},"useColorMode"," that handles this for you.\nYou just need to handle the addition and removal of the ",[82,367,348],{}," class on the body yourself, which I handled with a ",[82,370,371],{},"DarkModeSwitch"," ",[31,374,377],{"href":375,"rel":376},"https:\u002F\u002Fcode.ggrainger.uk\u002Fggrainger\u002FWebsite\u002Fsrc\u002Fbranch\u002Fmaster\u002Fapp\u002Fcomponents\u002FDarkModeSwitch.vue",[35],"component",[320,379,381],{"id":380},"content","Content",[16,383,384],{},"Nuxt Content allows you to configure multiple content sources, with different queryable fields.\nFor this site, I have divided the content into the following collections.\nI may end up extending this list if need different stores of data.",[65,386,387,405,416],{},[68,388,389,390],{},"Pages\n",[65,391,392,395],{},[68,393,394],{},"Pages to render on the site and show in the navigation bar.",[68,396,397,398,401,402,404],{},"Includes the ",[31,399,400],{"href":118},"home"," and ",[31,403,134],{"href":108}," pages.",[68,406,407,408],{},"Projects\n",[65,409,410],{},[68,411,412,413,415],{},"Projects to showcase on the ",[31,414,134],{"href":108}," page (like this one).",[68,417,418,419],{},"Socials\n",[65,420,421],{},[68,422,423],{},"Social links to show in the footer.",[320,425,427],{"id":426},"pages","Pages",[16,429,430,431,433,434,436],{},"Nuxt uses file based routing, where the file structure of the ",[82,432,426],{}," directory is used to generate routes for the site.\nThe ",[82,435,426],{}," directory is very simple for now, with only 3 templates in use.",[438,439,444],"pre",{"className":440,"code":442,"language":443},[441],"language-text","app\u002F\n└── pages\u002F\n    ├── projects\u002F\n    │   └── [slug].vue\n    ├── [...path].vue\n    └── dev.vue\n","text",[82,445,442],{"__ignoreMap":110},[16,447,448,451,452,455,456,458,459,462,463,465],{},[82,449,450],{},"dev.vue"," page is a development page used for testing components.\nThe ",[82,453,454],{},"[...path].vue"," page is a catch all that renders the matching page from the ",[82,457,426],{}," collection,\nand the ",[82,460,461],{},"projects\u002F[slug].vue"," page is used to render the matching project from the ",[82,464,134],{}," collection.",[20,467,469],{"id":468},"conclusion","Conclusion",[16,471,472,473,477,478,480],{},"Well there you have it, some background information on how this site was built.\nYou can check out the source code for this site ",[31,474,54],{"href":475,"rel":476},"https:\u002F\u002Fcode.ggrainger.uk\u002Fggrainger\u002FWebsite",[35],".\nMore ",[31,479,134],{"href":108}," to come soon (hopefully)!",{"title":110,"searchDepth":111,"depth":111,"links":482},[483,484,485,491],{"id":212,"depth":111,"text":213},{"id":236,"depth":111,"text":237},{"id":317,"depth":111,"text":318,"children":486},[487,489,490],{"id":322,"depth":488,"text":323},3,{"id":380,"depth":488,"text":381},{"id":426,"depth":488,"text":427},{"id":468,"depth":111,"text":469},"\u002Fimages\u002Fprojects\u002Fportfolio-website\u002Fcover.png","In this project, I discuss the creation of this portfolio website.",{},null,"\u002Fprojects\u002Fportfolio-website","2026-06-28",{"title":207,"description":493},"https:\u002F\u002Fcode.ggrainger.uk\u002Fggrainger\u002Fportfolio-website","projects\u002Fportfolio-website","tGwGUqYezbRU8mlLl5pnpgXNcg5gEDfh7NAd-GYRuHQ",1783154834690]