In the previous post we demonstrated how the design library was made including:
Now it's time to implement them in code. We followed the sequence of how the designs were made, starting with variables.
For colours, we created a separate colors.css
file in @repo/ui
, storing the colours as root variables in RGB format, imported them in index.css
, and extended the theme in tailwind-preset.ts
with these colours, referencing them via their variable names.
For the non-opaque colours we left out the alpha value to let tailwind set it.
Instead of explicit pixel values, we work in rem
s (reference to root element's font size - html), and used 16px
as the root font size. We had to convert the pixel values specified in Figma to rem
s (for example xl
font size is 32px
, which translates to 2rem
- 32 / 16).
The changes can be found here: GitHub link
We added the react-icons
package that contains the ionicons
package which we use in the designs.
Because it inserts the icons as svg
tags instead of img
, we can freely customise the colour and size of the icons.
Compare the changes on GitHub here: GitHub link
First, we created the tailwind styles for each typography element:
specifying their:
fontSize
variables)We created a low-level Text
component that is highly customizable. It supports a custom className
and accepts $variant
and $bold
props for flexibility.
We also created each typography element as individual components, using Text
, and specifying their respective $variant
s.
Check the code: GitHub link
To match the design system, we structured button styles based on:
Each of these was defined in a way that allows easy reuse and modification, to make sure buttons are consistent.
Instead of styling each button manually, we created a base component (ButtonBase
) that:
The main Button
component extends this by adding:
label
prop for text-based buttons wrapped in BodyRegular
.$size
and $variant
props to match designs.IconLeft
and IconRight
props to easily include icons.For even cleaner usage, we also defined preconfigured buttons:
<PrimaryButton />
<SecondaryButton />
<OutlineButton />
<GhostButton />
These provide an easy way to use the correct styles without repeating props, keeping the UI implementation simple.
To ensure buttons work as expected, we:
See the changes on GitHub: GitHub link
Similarly to buttons, we styled inputs based on the design system with:
We structured the input component into reusable parts:
InputBase
– Core input styling.InputWrapper
– Handles labels, notes, and errors.InputLabel
, InputNote
, InputError
– Text elements.The Input
component combines these elements, and alongside with native input
props, it supports additional props to match designs:
$variant
and $size
props for quick customization.label
, note
, and error
(can be hidden with hideError
).IconLeft
and IconRight
props to easily include icons.We also wrote unit tests to validate behaviour and added Storybook stories to preview all variations.
See the changes on GitHub: GitHub link
Checkbox variations are based on:
Since native checkboxes are difficult to style, we created a fake checkbox and linked it to the actual input
via label
. The checked state is handled through conditional styles.
We reused informational components from Input
, including:
InputNote
– Displays additional information.InputError
– Shows error messages when validation fails.Custom properties follow the same structure as Input
:
$size
to match the design system.label
, note
, and error
(with hideError
to disable error messages).As standard, we wrote unit tests and added Storybook stories to verify interactions, states, and accessibility.
This is how it looks like in code: GitHub link
As seen in the code, we avoided implementing controlled inputs and instead forwarded ref
s . The reason for this is to leverage native functionality as much as possible—uncontrolled inputs are more performant since they don't rely on React state or cause unnecessary re-renders.
(We will remove forwardRef
when upgrading to React 19, as it has been deprecated.)
The modal consists of two components:
Overlay
– A full-screen layer with a semi-transparent black background that blocks interaction with content behind it.Modal
– A component that appears in front of the overlay, drawing focus to user interactions or important content.The Modal
component is flexible in terms of content, allowing any children
to be passed inside. It includes predefined styles and optional elements:
title
– An optional heading at the top.hideCloseButton
.The modal closes when the overlay is clicked or when the close button is pressed.
We wrote unit tests, and stories for the modal. Check the code here: GitHub link
Summary of all changes in the code can be found here: GitHub link.
Tasks and subtasks for this setup are listed on our ClickUp board.
Full designs can be found in the Figma file:
All key project documentation is available in our ClickUp wiki.
We will use these components to build the application's screens, aligning them with the user stories and design specifications.
In the meantime, design will be working on the authentication screens:
Here's how our timeline look like now: