"Simplicity is the ultimate sophistication."
Starbase is a personal, opinionated boilerplate — it reflects deliberately chosen tools, patterns, and workflows. Decisions should favor informed preference over broad appeal. When in doubt, optimize for productivity and consistency with the existing stack, not generality. That said, preference never trumps code quality — clean, correct, well-structured code wins every time. Opinion only applies when the choice is genuinely a matter of taste.
This file captures conventions, patterns, and lessons learned from working on this project. It is meant to evolve — when we discover a useful pattern or convention worth preserving, suggest adding it here.
Inclusivity and accessibility are more important than any other aspect of the user experience. Unstyled, semantic HTML that works for everyone is better than a polished UI that excludes people.
- Target WCAG 2.2 AA compliance as the baseline for all work
- Use semantic HTML elements — prefer native behavior over custom JS (e.g.,
<button>over<div onClick>,<nav>over<div class="nav">) - All interactive elements must be keyboard-accessible and have visible focus indicators
- Images need meaningful
alttext (oralt=""for purely decorative images) - Color alone must never be the only way to convey information — pair it with text, icons, or patterns
- Ensure sufficient color contrast ratios (4.5:1 for normal text, 3:1 for large text)
- Test with screen readers when building new interactive patterns
- When in doubt between a flashy feature and an accessible one, choose accessible
- Always use aliased paths — never relative imports
from 'utils'notfrom '../../lib/utils/darkMode'from 'atoms'notfrom '../ui/atoms/Button'
- If a relative path seems necessary, that means a new alias is needed — update both
tsconfig.app.jsonpathsandvite.config.tsresolve.alias - See
tsconfig.app.jsonpathsfor the current list of aliases
- All utils live in
src/lib/utils/as individual files (e.g.,cn.ts,darkMode.ts) - Every util must be re-exported from
src/lib/utils/index.tsso consumers import from'utils' - Single-export utils use
export * from './cn'(flat import:import { cn } from 'utils') - Multi-function utils use
export * as darkMode from './darkMode'(namespaced import:import { darkMode } from 'utils', thendarkMode.applyTheme()) - Pattern: create the file, add the re-export to
index.ts, import via'utils'
All query/mutation options live in src/lib/queries/, organized by API domain — one file per domain (e.g., github.ts, users.ts, products.ts).
- Every domain file must be re-exported from
src/lib/queries/index.tsas a namespace:export * as github from './github' - Consumers import via the
'queries'alias:import { github } from 'queries', thengithub.repoQueryOptions() - Domain files group all related concerns: query options, mutation options, and associated types
- One file per API domain/data source, not one file per query — this is what keeps the structure scalable
- Pattern: create the domain file, add the namespaced re-export to
index.ts, import via'queries'
The re-export pattern depends on the content of the file:
- Single-export files → flat:
export * from './cn'(import as{ cn }) - Multi-export domain files → namespaced:
export * as github from './github'(import as{ github }, thengithub.thing())
Query domain files are always multi-export, so they always use namespaced re-exports.
Components follow Atomic Design and live in src/ui/:
- Atoms (
atoms/) — Smallest building blocks that can't be broken down further without losing meaning. HTML-level primitives: buttons, inputs, labels, links, icons. Each has its own distinct properties (size variants, color, font) but no dependency on other custom components. - Molecules (
molecules/) — Small groups of atoms functioning together as a single unit. A search form (label + input + button), a labeled toggle, a stat with an icon. Each molecule does one thing well — single responsibility. - Organisms (
organisms/) — Larger, distinct sections composed of molecules, atoms, or other organisms. A site header (logo + nav + search), a product grid, a comment thread. Organisms provide context — they show how smaller pieces work together in a meaningful section. - Templates (
templates/) — Page-level layout structures that arrange organisms and molecules into a content hierarchy. Focus is on spatial arrangement and content slots, not final content. Defines where things go and how they relate.
Reference: Atomic Design by Brad Frost
Each component level has a barrel file (index.ts) that re-exports all components. Every new component must be added to its level's barrel.
Components import from lower-level barrels for cross-level dependencies. For same-level siblings, use direct paths. This avoids circular dependencies.
// Molecule importing atoms (lower level barrel — safe)
import { Button, Input, Label } from 'atoms';
// Atom importing a sibling atom (same level — use direct path)
import { Button } from 'atoms/Button';
// Route file importing from atoms and molecules (barrel — safe)
import { Code, RouterLink } from 'atoms';
import { PageHeader } from 'molecules';The rule: never import from your own level's barrel — it re-exports you, creating a circular dependency.
Preferred libraries already in the project — reach for these before writing custom solutions or adding alternatives:
- js-cookie — Cookie read/write
- usehooks-ts — Common React hooks (prefer over writing custom hooks when a suitable one exists)
- clsx + tailwind-merge (via
cn()) — Conditional/merged class names - react-icons — Icon sets (currently using
react-icons/lufor Lucide icons) - @tanstack/react-query — Server state / data fetching
- @tanstack/react-router — Routing
- motion — Animation (import from
motion/react)
See package.json for the full dependency list.
When adding a new library, always do a fresh web search for the latest docs, changelog, and community status. Do not rely on cached or training data — APIs change, packages get deprecated, and better alternatives emerge.
- Theme prefix:
sb-(starbase) for all color tokens - CSS variables defined in
src/lib/theme/tailwind.css - Dark mode:
.darkclass on<html>, with flash-prevention script inindex.html - Cookie:
theme-preferencewith valueslight,dark, or absent (system default)
- Never reference conversations, past edits, or memories in code comments, commit messages, or documentation
- Keep everything matter-of-fact and readable for any future developer
- No "as we discussed", "per our earlier change", or similar phrasing
- Never commit, push, or run destructive git commands — read-only git operations (diff, status, log, etc.) are fine
- After every code change, provide a Conventional Commit message ready to copy+paste
- Format:
type(scope): description— e.g.,feat(ui): add dark mode toggle atom - Common types:
feat,fix,chore,style,refactor,docs,test - Keep the description concise and lowercase
- Format:
- This file is a living document — suggest additions when we land on a convention worth preserving
- Goal is to fine-tune how we collaborate over time so good patterns stick