agentleFS
Sign inSign up

golid / rules

golid-ai/golid/.cursor/rules/frontend-components.mdc

Patterns for creating and maintaining SolidJS components (atoms, molecules, organisms)

Cursor rule40 starsChanged 4 months ago
---
description: Patterns for creating and maintaining SolidJS components (atoms, molecules, organisms)
globs: frontend/src/components/**/*.tsx,frontend/src/components/index.ts
alwaysApply: false
---

# Component Creation Patterns

> **Thesis:** Components are stateless building blocks. Atoms compose into molecules, molecules into organisms. Every component exports from the barrel.

**Reference files:** `Button.tsx` (atom) and `Modal.tsx` (molecule).

## Atom vs Molecule vs Organism

- **Atom**: No dependencies on other components. Self-contained. (Button, Input, Chip, Avatar, StarRating)
- **Molecule**: Composes atoms. (Card, Modal, Tabs, Dropzone, Select, DestructiveModal)
- **Organism**: Composes molecules + atoms into a page section. (Navbar, Sidebar, Footer)

## File Structure

```
components/
├── atoms/
│   └── NewComponent/
│       └── NewComponent.tsx
├── molecules/
│   └── NewComponent/
│       └── NewComponent.tsx
├── organisms/
│   └── NewComponent.tsx
└── index.ts                    ← MUST export here
```

## Component Template

```tsx
import { type Component, splitProps, Show } from "solid-js";
import { cn } from "~/lib/utils";

export type NewComponentSize = "sm" | "md" | "lg";

export interface NewComponentProps {
  size?: NewComponentSize;
  class?: string;
  children?: JSX.Element;
}

const sizeStyles: Record<NewComponentSize, string> = {
  sm: "text-sm px-2 py-1",
  md: "text-base px-3 py-2",
  lg: "text-lg px-4 py-3",
};

export const NewComponent: Component<NewComponentProps> = (props) => {
  const [local, rest] = splitProps(props, ["size", "class", "children"]);
  const size = () => local.size ?? "md";

  return (
    <div class={cn(sizeStyles[size()], local.class)} {...rest}>
      {local.children}
    </div>
  );
};

export default NewComponent;
```

## Key Rules

### Styling
- Use **CSS variables** from `styles/theme.css` for colors — never hardcode hex values
- Use `cn()` (from `~/lib/utils`) for conditional class merging
- Support dark mode automatically via CSS variables (`text-foreground`, `bg-background`, etc.)
- Size variants (`sm`, `md`, `lg`) when the component has variable sizing

### Accessibility
- Interactive elements need `aria-label` or visible label
- Keyboard navigation: `onKeyDown` for Enter/Space on custom buttons
- Focus management: `tabindex`, `focus-visible:` styles
- Screen reader text: `<span class="sr-only">` for icon-only buttons

### Props
- Always export the props interface
- Use `splitProps` to separate local props from spread props
- Optional props have sensible defaults
- `class?: string` for style overrides

### Barrel Export
**Always add to `frontend/src/components/index.ts`:**

```tsx
export { NewComponent, type NewComponentProps } from "./atoms/NewComponent/NewComponent";
```

## Related Rules

- Lazy loading, demo-state traps, raw-input exceptions — see `frontend-components-advanced`.

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.