👋 Thanks for your interest in contributing!
- Node.js 18+
- npm 9+
# Install dependencies
make install
# Start development mode with hot reloading
make start-dev
# Start Storybook documentation
make start-docsAlways use Makefile commands rather than running npm/npx directly:
| Command | Description |
|---|---|
make start-dev |
Start development mode with hot reloading |
make start-docs |
Start Storybook documentation server |
make build |
Build the library for production |
make lint-fix |
Run linting and auto-fix issues |
make type-check |
Run TypeScript type checking |
- Particles - Lowest-level building blocks (Box, Text, Icon, etc.)
- Atoms - Interactive elements (Button, Checkbox, Link, etc.)
- Molecules - Composed components (Form, SingleLineInput, TabBar, etc.)
- Layouts - Arrangement components (Stack, Grid, Container, etc.)
- Wrappers - Style modifiers without DOM elements (BackgroundView, PaddingView, etc.)
Each component follows this structure:
src/{category}/{componentName}/
├── component.tsx # React component
├── styles.scss # SCSS styles (if needed)
├── index.ts # Exports
├── documentation.stories.tsx # Storybook stories (CSF3)
└── documentation.mdx # Storybook documentation
UI-React uses CSS @layer for predictable specificity:
@layer kiba-reset, kiba-structure, kiba-theme;- kiba-reset - Browser resets and normalization
- kiba-structure - Layout, display, positioning, sizing
- kiba-theme - Colors, borders, padding, fonts, shadows
Structure layer (kiba-structure):
display,flex-direction,align-items,justify-contentposition,width,height,overflowbox-sizing,flex-grow,flex-shrink
Theme layer (kiba-theme):
background-color,color,border-*padding,margin,border-radiusfont-*,box-shadow,opacitytransition-*,cursor
All variables follow the pattern --kiba-{category}-{name}:
// Component sets CSS variables for dynamic values
style={{ '--kiba-box-width': width }}
// SCSS uses them with fallbacks
width: var(--kiba-box-width, 100%);Variants are applied as CSS classes. Split multi-part variants:
// variant="primary-large" becomes classes: .primary .large
className={getClassName(Component.displayName, ...(variant?.split('-') || []))}.KibaButton {
// Default styles on base selector
background-color: transparent;
&.primary {
background-color: var(--kiba-color-brand-primary);
}
&.large {
padding: 1em 2em;
}
}All components extend IComponentProps:
import { IComponentProps } from '../../model';
export interface IButtonProps extends IComponentProps {
text: string;
onClicked?: () => void;
}IComponentProps provides: id, className, variant, style
All components must accept and merge the style prop:
const combinedStyles: React.CSSProperties = {
...props.style, // External styles first
// ... component's own styles
};Wrappers must not create wrapper DOM elements. Use WrapperView:
import { WrapperView } from '../wrappingComponent';
export function MyWrapper(props: IMyWrapperProps): React.ReactElement {
return (
<WrapperView
className={props.className}
style={props.style}
wrapperClassName={MyWrapper.displayName}
wrapperStyle={{ /* computed styles */ }}
>
{props.children}
</WrapperView>
);
}Only create styles.scss when a component needs:
- Static CSS class-based styling
- CSS layer definitions
- Variant classes
Wrappers that only pass dynamic styles (like BackgroundView) should NOT have SCSS files.
Every component must be documented, and every variant must have a story example.
This ensures:
- Users can see all available options
- Visual regression testing covers all states
- Theming customizations are discoverable
Each component needs two files:
import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from '.';
const meta: Meta<typeof MyComponent> = {
component: MyComponent,
title: 'Category/MyComponent',
};
export default meta;
type Story = StoryObj<typeof MyComponent>;
export const Default: Story = {
args: {
// default props
},
};
export const Variant: Story = {
args: {
variant: 'primary',
},
};import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
import * as Stories from './documentation.stories';
<Meta of={Stories} />
# MyComponent
**MyComponent** is a `category` component that does X.
<Canvas of={Stories.Default} />
<Controls of={Stories.Default} />
## Examples
### Variant Name
<Canvas of={Stories.Variant} />
## Theming
| Class | Description |
|-------|-------------|
| `.KibaMyComponent` | Base component styles |
| `.primary` | Primary variant |- Use TypeScript for all components
- Use
camelCasefor functions and variables - Use descriptive names over comments
- No newlines within functions
- Avoid comments that are easily inferred from code
- Use named parameters wherever possible
Before submitting a PR:
- Run
make lint-fixto fix linting issues - Run
make type-checkto verify types - Run
make buildto ensure production build works - Check Storybook with
make start-docs
To test changes in another project:
# In ui-react directory
npm link
# In your project directory
npm link @kibalabs/ui-reactThanks,
Krishan @krishan711