formica-ui-lib
v1.0.229
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.
Storybook buckets should be editor-friendly
Code folders follow atomic responsibility. Storybook titles should follow the organization that content editors and consumers use to browse components.
Common buckets include:
Molecules/CardsMolecules/SelectorsOrganisms/BannersOrganisms/CarouselsOrganisms/CollectionsOrganisms/GridsOrganisms/NavigationOrganisms/SectionsOrganisms/ProductInformation
Use Molecules/Cards for reusable *Card components. Use Organisms/Collections for *Collection components, Organisms/Grids for *Grid components, and Organisms/Sections for larger page-section wrappers.
Collections vs Sections
Use Organisms/Collections for organisms built around a repeated set of similar items: cards, swatches, document links, FAQs, filter groups, and similar repeatable content. A collection may have a title, intro body, or footer CTA, but those elements should frame the repeated items.
Use Organisms/Sections for page-level bands or composed areas. A section may combine different component types, own page-section spacing, provide controls or context, or wrap one or more child components into a page slice. A section can contain a collection, but it should not just duplicate the collection.
Rule of thumb: if removing the repeated items makes the component meaningless, it is probably a collection. If the component exists to create a page band, coordinate mixed content, or provide context around child components, it is probably a section.
Avoid duplicate bucket names across atomic levels when possible. For example, selector components should generally stay under Molecules/Selectors.
Avoid article-specific names for reusable components. Keep Article* names only when a component is truly article-specific, such as ArticleBanner.
Scene Stories
Scene stories are special.
They are Storybook-only composition shells, not React/Next integration surfaces. Use templates for React/Next page integration.
Use scene stories when you need to preview how separately exposed CMS/XMC components appear together, or when you need a lightweight simulation of how the UI will behave when wired into a BFF or application layer.
That means scene stories may own:
- layout spacing needed to judge the combined composition
- 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, reusable cards, selectors, and inputs - organisms richer composed sections like grids, navs, banners, collections, panels, and page sections
- 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.
