My Design System

Why I Created a Design System

I was using Material UI for a project, and the bundle size, combined with the css-in-js approach to styling, resulted in an application that was difficult to render on the server and that had a pretty poor initial load time. I wanted to create a design system that was similar to Material UI / Flutter but that emphasized semantic HTML and a small bundle size.

Also, when creating a Rich Text Editor with Lexical, I found that it was best to not use highly abstracted components, so I tried to create a library of simple components that can be easily rendered on the server and that doesn't use too much CSS and JavaScript.

In summary, the goal of the design system is to create a look that is comparable to other popular design libraries used today while minimizing load time, bundle size, and complexity.

Assumptions

The design system assumes that:

  • you want to use HTMX to create a SPA (Single Page Application),
  • you want to use semantic html, but that you want to style components primarily with CSS classes and inline styling,
  • your Content Security Policy allows for inline styling,
  • you want to use Floating UI for tooltips, menus, dropdowns, and the like,
  • you want to have slightly different layouts for desktop and mobile,
  • you want to prioritize mobile devices (you want the application that you show the user to always be similar to the mobile device view),
  • you do not want to listen to resize events on mobile

Explore the Design System

If you are looking for information about specific components or utility classes, use the links in the list below to find more information about those aspects of the system. Each aspect of the design system should describe the styling, accessability, and JavaScript concerns that go along with it.

If you are looking to learn more about the design system and the decisions made, continue reading below or navigate to the 'General' page for the design system.

If you want the code, navigate to the resources section.

Images, Videos, and Audio

Images

About Images
Example Image

JavaScript for Media Elements

The JavaScript for the HTML Video and Audio elements is all basically the same except for the Video element utilizing the Fullscreen API.

  • Both elements have range inputs for the playback time and the audio level which allows the user to control where they are in the playback of the media and the volume of the audio, respectively.
  • Both elements have buttons for playing the media, pausing the media, replaying the media when it is finished playing, and muting/unmuting the audio.
  • Both elements have select inputs for changing the playback rate of the media.
  • Both elements show the current time / the time left for the media.

When the user requests an action be done, the JavaScript attempts to complete the action, and the JavaScript listens to events signifying the completion of the action. When the action is completed, the user interface is updated. Here is a list of events that are listened to by the client-side JavaScript:

Abort Event
Changes the UI to show an error icon. Checks to see if there is warning text in the wrapper div and removes the hidden attribute if possible.
Can Play Event
Updates the UI to show the play button.
Can Play Through Event
Sets the data-play-through attribute on the video or audio tag to true.
Duration Change Event
Updates the UI of the playback slider to show the new duration.
Emptied Event
Not handled.
Ended Event
Updates the UI to show the user that they can replay the media.
Error Event
Updates the UI to show an error icon. Checks to see if there is error text within the audio/video wrapper, and if there is, remove the hidden attribute from it.
Loaded Data Event
Not handled.
Loaded Metadata Event
Updates the UI of the playback slider and timing indicator to show a proper duration of the media.
Load Start Event
Not handled.
Pause Event
Updates the UI to show the user that they can play the media.
Play Event
Updates the UI to show the user that they can pause the media.
Playing Event
Updates the UI to show that they can pause the media.
Progress Event
Not handled.
Rate Change Event
Not handled.
Seeked Event
Checks to see if the data-should-play attribute on the media element is equal to true. If it is, then plays the media.
Seeking Event
Sets the data-should-play attribute on the media element to true.
Stalled Event
Updates the UI to show a loading animation to the user.
Suspend Event
Not handled.
Time Update Event
Updates the time. This changes the appearance (background image) of the playback slider and updates the time remaining timer.
Volume Change Event
Updates the volume slider.
Waiting Event
Sets the data-resume attribute on the media element to true.
Video Click Event
Video Only: Plays the media if it is already playing. Else pauses it.
Video Focus Event
Video Only: Sets the data-focus attribute on the video wrapper element to true.
Video Mouse Enter Event
Video Only: Sets the data-hover attribute on the video wrapper element to true.
Video Mouse Leave Event
Video Only: Sets the data-hover attribute on the video wrapper element to false.
Fullscreen Button Click Event
Video Only: Toggles fullscreen mode using the Fullscreen API.

Audio and Video Elements on Mobile For mobile devices, there is no purpose in including the audio volume slider since the volume level is exclusively handled by the device. For this reason, the device type should be identified on the server using the request user-agent, and the audio / video element that you send to the user should be configured to the device. Look into UAParser.js if you are using a nodejs backend.

Audio

For the data-main-controls wrapper, make sure that the data-play button is the only icon that is not hidden initially.

USC Fight Song

0:00 / 0:00

Video

0:00 / 0:00

Reggie Bush Highlights

Here is an example of random Reggie Bush Highlights.

Splide Implementation

Testing out the Splide Implementation. I am not 100% sure if it is worth it to include Splide in the design system. It is easier to implement than a custom carousel and it is easier to use than what I have now for tabs / mobile steppers, but it also introduces 29kB of more code (which isn't much actually). Also, I'm testing out the splide implementation to create a HTML snippet for implementing these carousels. Size of the Splide Components:

CSS: JavaScript:
5kB Minified (7kB UnMinified) 29kB

Carousel Example 1:

  • Primary

  • Secondary

  • Info

  • Warning

  • Error

  • Success

  • Card

Carousel Example 2:

  • Mosaic 1
  • Mosaic 2
  • Mosaic 3

Resources

Click on the individual buttons to download separate files, or click on the All Files button to download a ZIP file of all the code. The code is sent pre-transpiled for readability.
Click on the VSCode Snippet button to download a copy of the html.json file that will make implementing this design system simpler.