Selected work
Reusable UI componentsIndependent demonstration
Component Lab
A small, working set of accessible components — buttons, form controls, tabs, a dialog, and a filterable table — built on shared tokens to show how I approach UI engineering.
- Context
- Independent demonstration built for this site. Not a client project or shipped product; all data is fictional.
- Date
- 2026
- My role
- Design and implementation
- Scope
- Design tokensComponent API designAccessibilityResponsive behaviour
The need
Portfolio screenshots show what a product looks like, but not how its components behave. This lab lets you use the components directly: tab through them, open the dialog, filter the table, and trigger each state.
Key workflows
- 01Navigate the whole demo with a keyboard, including tabs with arrow keys.
- 02Open and dismiss a dialog without losing your place on the page.
- 03Search and filter a table, and see loading, empty, and error states.
- 04Validate a form field and read the error where it occurs.
Try the components
Independent demonstration · fictional data
Buttons
Four variants, two sizes, and loading and disabled states.
| Name | Team | Status | Updated |
|---|---|---|---|
| Ada Fernandes | Design | Active | 2 Oct |
| Ravi Kulkarni | Engineering | Active | 29 Sep |
| Mei Tanaka | Product | Invited | 27 Sep |
| Jonas Berg | Engineering | Paused | 21 Sep |
| Priya Nair | Design | Active | 18 Sep |
| Leo Martins | Support | Invited | 12 Sep |
6 of 6 people shown.
Confirmation dialog
Opens a modal built on the native dialog element. Press Escape, click outside, or use the buttons to close it; focus returns to the trigger.
Design decisions
- Tokens before components
- Colour, radius, and spacing come from the same tokens as the rest of this site, so a component cannot drift from the system it lives in.
- Every state is designed
- Loading, empty, and error states are first-class variants of the table, each with a clear next action.
- Restraint
- One accent colour, one border weight, two radii. Hierarchy comes from type and spacing rather than decoration.
Engineering decisions and tradeoffs
- Native elements first
- Buttons are buttons, inputs have labels, and the dialog uses the native <dialog> element for focus handling and Escape-to-close, reducing custom code.
- ARIA where native elements stop
- Tabs follow the WAI-ARIA tabs pattern with roving tabindex and arrow-key support, because there is no native equivalent.
- Small, explicit component APIs
- Components take a variant and size rather than arbitrary class names, which keeps usage consistent across screens. Escape hatches exist but are not the default.
Stack
- React
- TypeScript
- Tailwind CSS
- Native <dialog>
- WAI-ARIA tabs pattern
What the demo includes
- Button variants with visible focus and disabled states.
- Labelled text input and select with inline validation.
- Keyboard-accessible tabs.
- A modal dialog using the native element.
- A searchable, filterable table with loading, empty, and error states.
Lessons
- Using the platform — <dialog>, <button>, <label> — removes a surprising amount of accessibility code.
- Designing empty and error states early changes the component's API, so it is worth doing first.