formica-ui-lib
v1.0.174
Published
This repository contains Formica's Storybook-based UI component library.
Readme
Formica UI
This repository contains Formica's Storybook-based UI component library.
The goal of this repo is to keep UI pieces:
- reusable
- easy to compose
- easy to wire into a BFF or app layer
- free from business logic
If you are new to the codebase, read this file first. If you need the stricter engineering rules, read CONTRIBUTING.md.
What Lives Here
This project is mainly for:
- atoms, molecules, organisms, templates, and scenes
- Storybook stories for those components
- shared prop contracts and callback payload types
This project is not where UI components should:
- fetch data
- handle routing
- talk directly to APIs
- make business decisions
Folder Basics
src/stories/...UI components and Storybook-facing implementationssrc/componentProps/...prop contracts and shared exported typesmocks/...mock data used by stories and scene simulations
Working Rule Of Thumb
Think of most UI components as display components.
Their job is to:
- render data
- accept callback props
- emit typed payloads upward
- keep only small visual state when needed
Their job is not to:
- decide where the app navigates
- call APIs
- manage app-wide state
- invent behavior on their own
Coding Standards In Plain English
1. Put props and types in their own files
If a component needs props, create a dedicated prop file for it under src/componentProps/....
Do not define the full prop contract inline inside the component unless it is truly private and temporary.
Good:
export type ImageGridProps = {
images: GridImage[];
onImageClick?: (payload: ImageGridClickPayload) => void;
};2. Keep UI components dumb
A UI component should mostly just render what it is given.
Good:
- render text, images, layout, and controls
- call
onClick,onChange,onSelect, etc.
Bad:
- calling
fetch - importing routing logic
- mutating app-wide data
- logging app behavior from deep inside a presentational component unless it is only a temporary local debug step
3. Pass behavior down, emit events up
The app layer, BFF simulation layer, or scene story should own behavior.
The deeper UI component should only emit a typed callback.
Example flow:
- story or scene owns the state
- scene passes callback props down
- item component calls the callback
- typed payload comes back up
4. Use explicit callback names
Prefer clear names over generic handlers.
Good:
onCartClickonLoginClickonPromoClickonImageClickonCountrySelect
Less preferred:
onActiononEventhandleThing
5. Do not use any
If a callback sends data upward, define a real payload type and export it.
Good:
export type ImageGridClickPayload = {
image: GridImage;
index: number;
};Storybook Standards
Stories should use .stories.js
Use .stories.js files for Storybook stories in this repo.
Stories should use args
Prefer args-based stories and simple templates.
Avoid complex JSX-heavy story files unless there is a real need.
Stories must match the final prop contract
If the component props change, update the story right away.
Do not leave stale story args, controls, or fake props around.
Scene Stories
Scene stories are special.
They should act like a lightweight simulation of how the UI will behave when wired into a BFF or application layer.
That means scene stories should often own:
- selected values
- visible/hidden state
- click handlers
- change handlers
- log output for interaction testing
In other words: if a child component needs a callback to feel real, the scene story should usually provide it.
Generic Layout Containers
We now have a few reusable layout primitives:
ContentContainerContentGridFlowRow
Use them when several components share the same layout pattern.
Examples:
- centered max-width wrappers
- responsive content grids
- rows of controls or logos with shared spacing
Do not force everything into one generic container.
A generic container is worth using when the shared part is structural:
- width
- spacing
- columns
- wrapping
- alignment
It is not the right tool when the component needs domain-specific rules.
Good use of a generic container
ImageGridusingContentGridDecorGridusingContentGrid
Bad use of a generic container
- a grid deciding what happens when a card is clicked
- a layout wrapper deciding navigation or selection logic
Responsive Layout Rule
Use one breakpoint for normal responsive work:
- unprefixed Tailwind classes are mobile below
md md:classes are PC atmdand above
Avoid sm:, lg:, xl:, and 2xl: layout variants unless the design explicitly requires an exception. Most responsive changes should live in templates and organisms, with fewer changes in molecules and very few in atoms.
When To Use ClickableCard
Use ClickableCard when a whole card or large visual surface acts as one click target.
Examples:
- an image tile that opens detail
- a swatch card where clicking the image opens the decor
Do not use ClickableCard when only a normal CTA inside the card is interactive.
Examples:
- a card with a single "Learn More" button
- a card with a standard text link
Important rule
The component that owns the clickable surface should own ClickableCard.
Good:
SwatchCardusesClickableCardImageGridusesClickableCardper image tile
Bad:
DecorGridwrapping every child inClickableCardContentGridtrying to handle clicks
Atomic Design Guidance
Try to keep responsibility at the right layer:
- atoms simple UI pieces and layout primitives
- molecules
small composed patterns like
ClickableCard - organisms richer composed sections like grids, navs, banners, panels
- scenes higher-level composition and interaction simulation
If a primitive starts learning too much about a domain concept, it is probably in the wrong layer.
Branching / Git Workflow
Direct commits to main are not allowed.
Create feature branches from dev.
Create a feature branch from dev
- Checkout
dev - Pull the latest changes
- Create a feature branch named
feature/YourFeature
git fetch
git checkout dev
git pull
git checkout -b feature/YourFeatureFinal Advice For Junior Devs
If you are unsure where code should live, ask these questions:
- Is this layout or behavior?
- Does this component need to know app logic, or just render?
- Is this click target owned by the item, or by the container?
- Is this type reusable enough to belong in
componentProps? - Would this be easier to understand if the story owned the state instead?
If you stay strict about those five questions, your changes will usually fit the codebase well.
For the stricter rule set, examples, and review checklist, see CONTRIBUTING.md.
