Would I leave you in trouble! 
I've moved a couple of things into the front-end code. You could, in fact move most of the function node into the front-end (browser) but I'll leave it where it is for now, there are some advantages for each method but I don't think it will make much difference for you.
However, the processing that I've added to the front-end automatically toggles the formatting and the value right in the browser, it would work even with no network - though, of course, the changes would not then be sent back to Node-RED. Though you should note that a future enhancement could indeed allow your wife to work offline and re-sync when reconnecting to your home network.
So here is an updated flow:
[{"id":"a0346bf4b7bb7225","type":"group","z":"3badb0a6906eef7f","name":"Example UIBUILDER Shopping List","style":{"fill":"#bfdbef","fill-opacity":"0.29","label":true,"color":"#3f3f3f"},"nodes":["b44745fc1423ceef","6c13b25075aa8740","dc6b78ac7a21a417","9210c72945bbf0c7","2b116aacfe15b942","5a4af508bd9df8ec","86c42d09e04ebcde","37358a4ea308b8d7","4f371f7bd744689a","0f0f214d14299d01","3bc4ade8f86c2ef8","76c929bb2cf2d76b","09169acb7d586cf7","a4f532159390254e","8f55878c5ad7398a"],"x":134,"y":3279,"w":1088,"h":422},{"id":"b44745fc1423ceef","type":"link out","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"Out to shopping list","mode":"link","links":["2b116aacfe15b942"],"x":715,"y":3540,"wires":[]},{"id":"6c13b25075aa8740","type":"inject","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"Shopping list initialise","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"initialise-shopping-list","payload":"[{\"title\":\"Bread\",\"isChecked\":false,\"icon\":\"\",\"id\":100},{\"title\":\"Milk\",\"isChecked\":true,\"icon\":\"\",\"id\":101},{\"title\":\"Honey\",\"isChecked\":false,\"icon\":\"\",\"id\":102},{\"title\":\"Flour\",\"isChecked\":false,\"icon\":\"\",\"id\":103}]","payloadType":"json","x":280,"y":3540,"wires":[["dc6b78ac7a21a417"]]},{"id":"dc6b78ac7a21a417","type":"function","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"Low-code output","func":"// If the input msg is the initialise msg...\nif (msg.topic === 'initialise-shopping-list') {\n // ... Initialise/update the context variable\n context.set('shoppingList', msg.payload)\n}\n\n// Get the shopping list input data\nconst inputData = context.get('shoppingList') ?? []\n\n// Create the shopping list template\n// using UIBUILDER's low-code output json\n// NOTE: Copied from the output of a `uib-element` node\n// set to output a list element.\nconst lowCode = {\n \"topic\": \"shopping-list\",\n \"mode\": \"update\",\n \"_ui\": [{\n \"method\": \"replace\",\n \"components\": [{\n \"type\": \"div\",\n \"id\": \"shopping-list\",\n \"parent\": \"#more\",\n \"position\": \"last\",\n \"attributes\": {\"aria-labelledby\": \"eltest-ul-ol-heading\"},\n \"components\": [\n {\n // Remove this object if no heading wanted\n \"type\": \"h2\",\n \"id\": \"shop-list-heading\",\n // Change the list heading here:\n \"slot\": \"Shopping List\",\n \"components\": [],\n },\n {\n // The actual shopping list\n \"type\": \"ul\",\n \"id\": \"shoplist\",\n // List entries will go here\n \"components\": [],\n },\n ],\n }],\n }],\n}\n\n// Grab a ref to the list entries components array\nconst shopList = lowCode._ui[0].components[0].components[1].components\n\n// Translate the input data to list entries.\n// Assumes the data is an array on msg.payload\ninputData.forEach( entry => {\n const classes = []\n const styles = {\n // padding: \"1em 0.5em\"\n }\n\n // styles['list-style'] = '\"✖️\"'\n\n if (entry.isChecked) {\n classes.push('success') // green background\n // styles['font-weight'] = \"bold\"\n // styles['list-style'] = '\"✅\"'\n } else {\n classes.push('surface3')\n }\n\n shopList.push({\n \"type\": \"li\",\n // We can set any attribute here\n \"attributes\": {\n // Use the default uib-brand.css classes\n \"class\": classes.join(' '),\n \"style\": mergeStyles(styles),\n // Invert the value and send back to Node-RED\n \"onclick\": \"handleListItemClick(event)\",\n // \"ontouchend\": \"handleListItemClick(event)\",\n // Track as data attributes for easier processing\n \"data-title\": entry.title,\n \"data-value\": entry.isChecked,\n \"data-id\": entry.id,\n },\n // This is the content of the list entry - can be HTML.\n \"slot\": entry.title,\n })\n})\n\n// Send the message\nreturn lowCode\n\n// stylename: stylevalue; ...\nfunction mergeStyles(styles) {\n let mergedStyles = ''\n Object.keys(styles).forEach ( styleName => {\n mergedStyles += `${styleName}: ${styles[styleName]}; `\n })\n return mergedStyles\n}\n\n/**\n * input data schema:\n * {\"title\":\"Bread\",\"isChecked\":false,\"idon\":\"\",\"id\":100}\n */","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":3540,"wires":[["b44745fc1423ceef"]]},{"id":"9210c72945bbf0c7","type":"uibuilder","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"","topic":"","url":"shopping-list","okToGo":true,"fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"blank","extTemplate":"","showfolder":false,"reload":false,"sourceFolder":"src","deployedVersion":"7.3.0","showMsgUib":false,"title":"","descr":"","editurl":"vscode://file/src/uibRoot/shopping-list/?windowId=_blank","x":300,"y":3360,"wires":[["3bc4ade8f86c2ef8"],["37358a4ea308b8d7"]]},{"id":"2b116aacfe15b942","type":"link in","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"In to shopping list","links":["b44745fc1423ceef","ddd1f248655218fc"],"x":185,"y":3360,"wires":[["9210c72945bbf0c7"]]},{"id":"5a4af508bd9df8ec","type":"debug","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"debug 9","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":715,"y":3340,"wires":[],"l":false},{"id":"86c42d09e04ebcde","type":"debug","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"debug 11","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":715,"y":3420,"wires":[],"l":false},{"id":"37358a4ea308b8d7","type":"switch","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"Detect client connections","property":"uibuilderCtrl","propertyType":"msg","rules":[{"t":"eq","v":"client connect","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":530,"y":3400,"wires":[["4f371f7bd744689a"],["86c42d09e04ebcde"]]},{"id":"4f371f7bd744689a","type":"link out","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"shopping list client connect","mode":"link","links":["0f0f214d14299d01"],"x":685,"y":3380,"wires":[]},{"id":"0f0f214d14299d01","type":"link in","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"from shopping list client connect","links":["4f371f7bd744689a"],"x":365,"y":3500,"wires":[["dc6b78ac7a21a417"]]},{"id":"3bc4ade8f86c2ef8","type":"switch","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"Process list clicks","property":"_ui.nodeName","propertyType":"msg","rules":[{"t":"eq","v":"LI","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":510,"y":3340,"wires":[["76c929bb2cf2d76b"],["5a4af508bd9df8ec"]]},{"id":"76c929bb2cf2d76b","type":"link out","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"link out 34","mode":"link","links":["09169acb7d586cf7"],"x":655,"y":3320,"wires":[]},{"id":"09169acb7d586cf7","type":"link in","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"link in 4","links":["76c929bb2cf2d76b"],"x":285,"y":3660,"wires":[["a4f532159390254e"]]},{"id":"a4f532159390254e","type":"debug","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","name":"debug 8","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":445,"y":3660,"wires":[],"l":false},{"id":"8f55878c5ad7398a","type":"group","z":"3badb0a6906eef7f","g":"a0346bf4b7bb7225","style":{"stroke":"#999999","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["37e4f2c0838f124e","67eb7bc082b134aa","ddd1f248655218fc","0c2799ed68acea4a"],"x":814,"y":3319,"w":382,"h":122},{"id":"37e4f2c0838f124e","type":"inject","z":"3badb0a6906eef7f","g":"8f55878c5ad7398a","name":"Light","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"mark-light","payload":"{\"class\":\"light\"}","payloadType":"json","x":910,"y":3360,"wires":[["67eb7bc082b134aa"]]},{"id":"67eb7bc082b134aa","type":"uib-update","z":"3badb0a6906eef7f","g":"8f55878c5ad7398a","name":"Set mode","topic":"","mode":"update","modeSourceType":"modeType","cssSelector":"html","cssSelectorType":"str","slotSourceProp":"","slotSourcePropType":"msg","attribsSource":"payload","attribsSourceType":"msg","slotPropMarkdown":false,"x":1060,"y":3360,"wires":[["ddd1f248655218fc"]]},{"id":"ddd1f248655218fc","type":"link out","z":"3badb0a6906eef7f","g":"8f55878c5ad7398a","name":"link out 35","mode":"link","links":["2b116aacfe15b942"],"x":1155,"y":3360,"wires":[]},{"id":"0c2799ed68acea4a","type":"inject","z":"3badb0a6906eef7f","g":"8f55878c5ad7398a","name":"Dark","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"mark-dark","payload":"{\"class\":\"dark\"}","payloadType":"json","x":910,"y":3400,"wires":[["67eb7bc082b134aa"]]}]
For this version to fully work, after you have set the URL and deployed, open the uibuilder node and go to the "Files" tab and replace the 3 files with the following:
index.html
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="../uibuilder/images/node-blue.ico">
<title>Shopping List - Node-RED uibuilder</title>
<meta name="description" content="Node-RED uibuilder - Shopping List">
<!-- Your own CSS (defaults to loading uibuilders css)-->
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
<!-- Load the uibuilder library and do other front-end processing -->
<script type="module" async src="./index.js"></script>
</head><body class="uib">
<h1 class="with-subtitle">Home Shopping List</h1>
<div role="doc-subtitle">Using the uibuilder ESM library.</div>
<div id="more"><!-- '#more' is used as a parent for dynamic HTML content in examples --></div>
</body></html>
index.js
// Using this as an ES6 module
// Import the uibuilder library first
// @ts-ignore
import uibuilder from '../uibuilder/uibuilder.esm.min.js'
// Handle list item click/touch events
window['handleListItemClick'] = function handleListItemClick(event) {
const item = event.currentTarget
item.value = !item.value // Toggle the value (true/false)
item.classList.toggle('success') // Toggle the active class
item.classList.toggle('surface3') // Toggle the active class
console.log('Item clicked:', item)
const msg = {
topic: 'listItemClicked',
payload: {
title: item.dataset.title,
value: item.value,
id: item.dataset.id,
}
}
// Send the clicked item's data to Node-RED
uibuilder.send(msg)
}
index.css
/* Load defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/uib-brand.min.css`
* This is optional but reasonably complete and allows for light/dark mode switching.
*/
@import url("../uibuilder/uib-brand.min.css");
/**
* NOTE: I'm using nested selectors here - they require fairly new browsers.
* You will have to unpack them if using a browser on an older phone.
*/
#shop-list-heading { display: none; } /* hide the default heading */
ul#shoplist { /* ul with id of shoplist */
padding-left: 0;
cursor: pointer;
& li {
/* Turn off normal list entry icons so we can control them */
list-style-type: none;
/* top/bottom, left/right - add padding so easier for user to touch on a phone */
padding: 0.5em 0;
border-top: 3px solid var(--surface2);
}
& li.success { /* list entry with class of success */
font-weight: bold;
}
/* Manually add a list entry icon so we can resize it - this is the default (not selected) version */
& li::before {
content: "✖️";
font-size: 2rem;
vertical-align: middle;
/* line-height: 20px; */
padding-right: 0.5em;
}
/* this is the selected version */
& li.success::before {
content: "âś…";
}
}
The CSS is already set up to use either light or dark mode. The HTML is simply changing the titles and loading the front-end JavaScript. The JavaScript loads the uibuilder client library and creates a simple event handler function that updates things on click/touch and sends the data back to Node-RED.
Buttons are also easy - maybe tomorrow if you've not been able to work them out.
An enhanced version of this is likely to end up as another example in the library for a future uibuilder release. 
Undoubtedly, this looks a bit overwhelming to start with. However, I've tried to annotate everything so keep reading through things and I think you will soon get a feeling for how everything works and will start to think of improvements. Just take things a step at a time and above all, have fun experimenting, it is very addictive!
Oh, nearly forgot. To do list for the to-do list!
Oops, the returned data is not yet updating the context variable.
- Sort the list?
- Add the buttons.
- Toggle filter to only selected or all entries
- Including one to add new entries
- More advanced:
- Add a right-click handler to each list entry that allows the entry to be deleted.

- Add a drag and drop handler to allow reordering of the list.
- Ask yourself why you bothered with a database when a retained context variable would do the job.