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
NOTE: This design system uses CSS and JavaScript that are tied together - i.e., some classes are required for components' JavaScript to work correctly. The design system bundles and transpiles client-side JavaScript using webpack, which is why you might want to download the JavaScript and CSS files from the resources section and tweak them / re-transpile them for your own use. If you make changes to the CSS, I recommend you use PostCSS so your changes will appear the same across all browsers.
- 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.
Note that I am still working on documenting the design system. I got tired of writing the documentation and wanted to move onto other things. I will come back and finish / improve the documentation eventually.
Images, Videos, and Audio
Images
About Images
<dialog data-esc="" id="image-dialog">
<div class="flex-row justify-start align-center">
<div class="flex-row grow-1 justify-center align-center w-100">
<span id="image-modal-text" class="h6 bold"></span>
</div>
<span class="grow-0 flex-row align-center">
<kbd class="kbd mr-1" data-popover="" data-pelem="#close-image-dialog" data-mouse="">ESC</kbd>
<button aria-label="Close Image Dialog" class="icon medium close-dialog" title="Close Image Dialog">
<svg class="t-normal" focusable="false" aria-hidden="true" viewBox="0 0 24 24">
<path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z">
</path>
</svg>
</button>
<div class="tooltip body2" role="tooltip" id="close-image-dialog">
Press ESC to Close the Dialog
</div>
</span>
</div>
<img src="" alt="" id="image-modal-image" class="mt-2 no-click">
<p class="body1 text-align-center" style="max-width: 600px; margin: 6px auto;" id="image-modal-additional-description"></p>
<details id="report-image" class="mt-2 medium p-md">
<summary>
<span class="t-warning h6 bold">
Report Inappropriate Content
</span>
<svg class="details" focusable="false" aria-hidden="true" viewBox="0 0 24 24">
<path d="M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z">
</path>
</svg>
</summary>
<form id="report-image-main" class="p-md">
<div class="accordion-content">
<div class="input-group block mt-1" data-hover="false" data-focus="false" data-error="false" data-blurred="false">
<label for="inappropriate-image-input">Inappropriate Image Description:</label>
<div class="mt-1 text-input block medium">
<textarea name="inappropriate-image-input" id="inappropriate-image-input" placeholder="Please tell us why this image should be removed from this application. Input must be between 50 and 200 characters long. Your input will be reviewed, and we will make a decision to remove the image based on our content policy." rows="10" maxlength="200" minlength="50"></textarea>
</div>
<p class="char-counter" data-input="inappropriate-image-input">
<span class="min-counter body2" data-val="50" aria-hidden="false">more characters required</span>
<span class="max-counter body2" data-val="200" aria-hidden="true">Characters Remaining</span>
</p>
</div>
<menu role="toolbar" class="flex-row align-center justify-end mt-1">
<button class="outlined success medium icon-text mr-1">
<svg focusable="false" aria-hidden="true" viewBox="0 0 24 24" tabindex="-1" title="SendSharp"><path d="M2.01 21 23 12 2.01 3 2 10l15 2-15 2 .01 7z"></path></svg>
<span>SUBMIT</span>
</button>
</menu>
</div>
</form>
</details>
</dialog>
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
Video
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:
Carousel Example 2:
Search Icons
About Icon Search
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.