lim jia sheng,

.Application Design II

task[3]: App prototype


  • Create interactive prototype of app using web technologies


Setting up

There were a few sagas to this journey, but we'll get to them when we get to them. First things first was definitely just setting up. Nothing eventful there, copy files, updating dependencies... until I found out SvelteKit introducing yet another breaking change that affected the configuration, & that I had to spend another half an hour finding out how to migrate. Oh yeah, have I mentioned I was using Svelte/SvelteKit & TypeScript for this project? Alongside all my own in-house built components.

Now, I'll be the first one to admit that using so much in-house built tech, especially built on top of an ever changing platform, is a recipe for abandonware, but in my defence... this SvelteKit migration was to v1.0, which means probably no more breaking changes for a bit.

Then, after all that, I began to build.


Very quickly after setting things up, it occurred to me that I needed a better way to get icons into my files. I was originally using a slightly wonky build plugin that took from @material-design-icons/svg, while reading from the import specifier to determine which icons were exported. This meant a really uncanny setup, that was sorta typesafe, but often not; sorta treeshake-able, but practically not. With that then, I was determined to build something that I could use for the rest of time.

That then, cumulated into maic:

Figure 1.1.1, maic's README, n.d


Well, well, well. We meet again, my arch nemesis — <BottomSheet>!

On platforms where this pattern is natively supported & implemented (i.e. Android & iOS), it's a breeze to use, & supremely helpful for UIs with multiple layers of nested screens, & users which might want to swap between them. However, on the web, one would have to implement this from scratch.

Drag physics

The first challenge of implementing a <BottomSheet> from scratch involves the dragging part of it, since the concepts of floating over everything & coming up from the bottom are mostly trivial. With a little poking & prodding, I managed to get something that would follow my finger as I dragged, using no less than mousedown, touchstart, mousemove, touchmove, mouseup, touchend, touchcancel, & drag events. With a little more prickling & pushing, I managed then to get it to snap to a fixed height when I let go, using CSS transitions.


For states, a <BottomSheet> has a surprising large number of them, since it would need to be able to:

  • occupy the full height
  • occupy a peek height
  • be hidden

& for each of those, we'd need to be able to tell whether it was:

  • currently being dragged by the user
  • currently settling into the state, after being dragged by the user
  • currently at the state

Thus, armed with my zero formal computing knowledge, I decided to use bits to describe them. More specifically:

BottomSheet state enum, n.d

Figure 1.1.2, BottomSheet state enum, n.d

Any consumer of the state of the <BottomSheet>, would then be able to check any of the above combination of states by simply doing a bitwise operation (i.e. state & BottomSheetStates.PEEK). This operation is actually used prominently in the internal guts of my <BottomSheet> implementation, especially since its usage ergonomics significantly improved by Svelte's reactivity (no need for any stores/events, just variable assignments & $:).

Touch capturing

At this point in time, things are still kind of wonky without touch capturing/swallowing when interacting with the BottomSheet. Currently, when you drag it to collapse it or hide it, the background content would receive the scroll events as well. There would also be a need to support scrollable content within the <BottomSheet>, & the lack of this feature would be a major blocker.

Whilst I don't think I have completely solved the issue, I think I've come close enough for it to not be noticeable anymore. So, how did I do it? A very precise combination of touch-action: none, Event.preventDefault() on the first touchmove/mousemove event, & Event.stopPropagation(). Which one does what? I have already forgotten, & I will not touch any of it.

<Mod>, <Expandable>, & <WhenDo>

<Mod> is the end goal, containing an <Expandable>, containing a <WhenDo>. All this will then form the repeated component found throughout the initial prototype, where it would preview a module, & allow the user to access it.

  • image-20221214230810531
  • image-20221214230732640

Figure 1.1.5, Prototype vs implementation, n.d

<WhenDoEdit> & <WhenDoAdd>

These would showcase the core functionality of editing a when or a do action in a module, as well as adding them. They would appear as <BottomSheet>s as well, but hook into the state of it to display a warning if the user attempts to dismiss it before saving their changes.

Prompt when a user accidently exits without saving, n.d

Figure 1.1.6, Prompt when a user accidently exits without saving, n.d


Figure 1.2.1, Prototype of nomaid, 4/11/2022


This project was pretty interesting. It was quite a lot more technical than anything I've done throughout my BDCM journey. However, I'm not sure how much of that was due to my own doings, rather than it being a requirement. I guess when left to my own devices, This was the most optimum way I could find to create the best outcome I could.

The main things I've learnt here was in the details. Creating maic was a tangent not gotten into here, as I felt it wasn't that applicable, but it was quite a rabbit hole, including its build process, as well as its integration to consumers' build processes via a vite/rollup plugin. That & also creating the darn <BottomSheet> was truly an exercise in knowledge & determination.

Not only do I have the additional technical enlightenment that will contribute to my future web development ventures to take away, I also have a whole self-sustaining Material Icons NPM package, plus a <BottomSheet> implementation I can finally reuse till the dusk of time.