Cursor rule
.cursor/rules/20-hermes-app-components.mdcCursor rules
Quality
65/100
Scores the file, not the repository.Length
707 words
27 headings · 16 code blocksRepository
45
— · pushed 3 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Hermes App - React Components Rules78## Component Structure910```11components/12├── ui/ # shadcn/ui components (styled primitives)13├── layout/ # Layout components14├── auth/ # Authentication components15├── download/ # Download-related components16├── queue/ # Queue view components17└── settings/ # Settings components18```1920## Component Definition2122```typescript23interface ComponentNameProps {24 title: string;25 onAction?: () => void;26 items?: Item[];27 className?: string;28}2930export function ComponentName({31 title,32 onAction,33 items = [],34 className,35}: ComponentNameProps) {36 return (37 <div className={cn("base-classes", className)}>38 {title}39 </div>40 );41}42```4344### Rules45- Use functional components with TypeScript46- Component names use PascalCase47- File names match component names48- Export as named export, not default49- Use hooks for state management50- Keep components focused and single-purpose5152## Props and TypeScript5354### Props Definition55```typescript56interface ButtonProps {57 /** The button's display text */58 label: string;59 /** Optional click handler */60 onClick?: () => void;61 /** Button visual style */62 variant?: "primary" | "secondary" | "destructive";63 disabled?: boolean;64 className?: string;65}6667export function Button({68 label,69 onClick,70 variant = "primary",71 disabled = false,72 className,73}: ButtonProps) {74 // Implementation75}76```7778### Event Handlers79- Prefix with `on`: `onClick`, `onChange`, `onSubmit`80- Use proper event types: `React.MouseEvent`, `React.ChangeEvent`8182### Children Props83```typescript84interface ContainerProps {85 children: React.ReactNode;86 className?: string;87}88```8990## shadcn/ui Integration9192### Using UI Components93```typescript94import { Button } from "@/components/ui/button";95import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";96import { cn } from "@/lib/utils";9798export function FeatureCard({ title, className }: Props) {99 return (100 <Card className={cn("w-full", className)}>101 <CardHeader>102 <CardTitle>{title}</CardTitle>103 </CardHeader>104 <CardContent>105 <Button>Action</Button>106 </CardContent>107 </Card>108 );109}110```111112- Import from `@/components/ui/`113- Don't modify UI component files directly114- Compose UI components to build features115- Use `cn()` for className merging116117## Styling with Tailwind CSS118119### Class Organization120```typescript121<div className={cn(122 // Layout123 "flex flex-col md:flex-row gap-4",124 // Spacing125 "p-4 m-2",126 // Typography127 "text-sm font-medium",128 // Colors129 "bg-background text-foreground",130 // Effects131 "rounded-lg shadow-md hover:shadow-lg",132 // Conditional133 isActive && "bg-accent",134 className135)}>136```137138### Theme Variables139Use CSS variables for theme colors (support light/dark):140```typescript141<div className="bg-background text-foreground border-border">142<div className="bg-primary text-primary-foreground">143<div className="bg-muted text-muted-foreground">144```145146### Responsive Design147```typescript148<div className={cn(149 "grid grid-cols-1", // Mobile150 "md:grid-cols-2", // Tablet151 "lg:grid-cols-3", // Desktop152 "xl:grid-cols-4" // Large153)}>154```155156## State Management157158### Local State159```typescript160const [count, setCount] = useState<number>(0);161const [items, setItems] = useState<Item[]>([]);162163// Functional update164setCount((prev) => prev + 1);165setItems((prev) => [...prev, newItem]);166```167168### Effects169```typescript170useEffect(() => {171 const subscription = subscribeToData();172 return () => subscription.unsubscribe();173}, [dependency]);174```175176### Server State (TanStack Query)177```typescript178import { useQuery } from "@tanstack/react-query";179180export function DownloadList() {181 const { data: downloads, isLoading, error } = useQuery({182 queryKey: ["downloads"],183 queryFn: fetchDownloads,184 });185186 if (isLoading) return <LoadingSpinner />;187 if (error) return <ErrorDisplay error={error} />;188189 return <div>{/* Render downloads */}</div>;190}191```192193## Component Composition194195### Container/Presentational Pattern196```typescript197// Container - handles data198export function DownloadListContainer() {199 const { data, isLoading } = useQuery({200 queryKey: ["downloads"],201 queryFn: fetchDownloads,202 });203204 const handleDelete = (id: string) => { /* ... */ };205206 return (207 <DownloadListPresentation208 downloads={data}209 isLoading={isLoading}210 onDelete={handleDelete}211 />212 );213}214215// Presentation - pure rendering216function DownloadListPresentation({ downloads, isLoading, onDelete }: Props) {217 // Pure rendering logic218}219```220221## Accessibility222223### Semantic HTML224- Use appropriate HTML elements225- Use `<button>` for actions, `<a>` for navigation226- Don't use `<div>` for interactive elements227228### ARIA Attributes229```typescript230<button231 aria-label="Delete download"232 aria-pressed={isActive}233 aria-disabled={isDisabled}234>235 <TrashIcon />236</button>237238<div role="status" aria-live="polite">239 {statusMessage}240</div>241```242243### Keyboard Navigation244```typescript245const handleKeyDown = (e: React.KeyboardEvent) => {246 if (e.key === "Enter" || e.key === " ") {247 e.preventDefault();248 onClick();249 }250};251```252253## Performance254255### Memoization256```typescript257import { useMemo, useCallback, memo } from "react";258259// Memoize calculations260const sortedItems = useMemo(261 () => items.sort((a, b) => a.name.localeCompare(b.name)),262 [items]263);264265// Memoize callbacks266const handleClick = useCallback(() => {267 doSomething(id);268}, [id]);269270// Memoize components271export const ExpensiveComponent = memo(function ExpensiveComponent({ data }: Props) {272 return <div>{/* ... */}</div>;273});274```275276## Loading States277278```typescript279import { Skeleton } from "@/components/ui/skeleton";280281if (isLoading) {282 return <Skeleton className="h-10 w-full" />;283}284285// Conditional rendering286{isLoading && <LoadingSpinner />}287{error && <ErrorDisplay error={error} />}288{data && <DataDisplay data={data} />}289```290291
Also in TechSquidTV/Hermes
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| TechSquidTV/Hermes.cursor/rules/00-project.mdc · 45 | Cursor rules | setuplint-formatstylearch+4 | 89/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-app.mdc · 45 | Cursor rules | lint-formatstylearchtypes+5 | 88/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-api.mdc · 45 | Cursor rules | stylearchdependenciesapi+2 | 77/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-db.mdc · 45 | Cursor rules | teststylearchtesting-strategy+3 | 73/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-hooks.mdc · 45 | Cursor rules | lint-formatstylearchtypes+3 | 73/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-app-routes.mdc · 45 | Cursor rules | archapiuido-not | 65/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-docker.mdc · 45 | Cursor rules | setupbuildstylesecurity+4 | 84/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-docs.mdc · 45 | Cursor rules | setuplint-formatstylearch+3 | 81/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/30-tests.mdc · 45 | Cursor rules | buildteststylearch+4 | 85/100 | 3 days ago |
Diff against .cursor/rules/00-project.mdc Diff against .cursor/rules/10-hermes-api.mdc Diff against .cursor/rules/10-hermes-app.mdc Diff against .cursor/rules/20-hermes-api-api.mdc Diff against .cursor/rules/20-hermes-api-db.mdc Diff against .cursor/rules/20-hermes-api-tests.mdc Diff against .cursor/rules/20-hermes-app-hooks.mdc Diff against .cursor/rules/20-hermes-app-routes.mdc Diff against .cursor/rules/30-docker.mdc Diff against .cursor/rules/30-docs.mdc Diff against .cursor/rules/30-tests.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
