Skip to content

Commit 978016a

Browse files
committed
convert cursor rules to skills
1 parent 0ff5637 commit 978016a

12 files changed

Lines changed: 1385 additions & 0 deletions

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
---
2+
name: accessibility
3+
description: Accessibility patterns and requirements for this project. Use when creating interactive components, forms, dialogs, or any user-facing UI.
4+
---
5+
6+
# Accessibility Patterns
7+
8+
This project builds on **Radix UI** primitives which provide strong built-in a11y. Follow these patterns to maintain that standard.
9+
10+
## ARIA Labels
11+
12+
All interactive elements without visible text must have an `aria-label`:
13+
14+
```typescript
15+
// Icon buttons
16+
<Button variant="ghost" aria-label="Home">
17+
<Icon name="Home" />
18+
</Button>
19+
20+
// Folder toggles
21+
<div role="button" aria-expanded={isOpen} aria-label={`Folder: ${folder.name}`}>
22+
```
23+
24+
## Form Accessibility
25+
26+
Link inputs to labels and error messages:
27+
28+
```typescript
29+
<Input
30+
id={id}
31+
aria-invalid={!!error}
32+
aria-describedby={`${id}-hint`}
33+
/>
34+
{!!error && <Text tone="critical">{error.join("\n")}</Text>}
35+
<div id={`${id}-hint`}>{hint}</div>
36+
```
37+
38+
Use the Radix UI `Label` component for proper form associations.
39+
40+
## Keyboard Navigation
41+
42+
### Required keyboard support for custom interactive elements:
43+
44+
- **Enter/Space**: Activate buttons and toggles
45+
- **Escape**: Close dialogs, cancel editing, deselect
46+
- **Tab**: Move focus between interactive elements
47+
48+
```typescript
49+
// Standard pattern for inline editors
50+
onKeyDown={(e) => {
51+
if (e.key === "Enter" && !e.shiftKey) {
52+
e.preventDefault();
53+
save();
54+
} else if (e.key === "Escape") {
55+
e.preventDefault();
56+
cancel();
57+
}
58+
}
59+
```
60+
61+
### Non-button clickable elements need `role="button"` and `tabIndex={0}`:
62+
63+
```typescript
64+
<div role="button" tabIndex={0} title="Click to edit">
65+
{content}
66+
</div>
67+
```
68+
69+
### Dialogs must prevent keyboard shortcuts from leaking to the canvas:
70+
71+
The dialog component already handles this — use `preventKeyboardPropagation` prop when needed. This stops Ctrl+A, Ctrl+C, Ctrl+V, Tab, Enter, and Escape from reaching React Flow.
72+
73+
## Screen Reader Support
74+
75+
Use `sr-only` class for visually hidden but screen-reader-accessible text:
76+
77+
```typescript
78+
// Close buttons with only an icon
79+
<DialogPrimitive.Close>
80+
<Cross2Icon />
81+
<span className="sr-only">Close</span>
82+
</DialogPrimitive.Close>
83+
84+
// Command palette title
85+
<DialogHeader className="sr-only">
86+
<DialogTitle>{title}</DialogTitle>
87+
<DialogDescription>{description}</DialogDescription>
88+
</DialogHeader>
89+
```
90+
91+
## Semantic HTML
92+
93+
Use proper semantic elements and ARIA roles:
94+
95+
```typescript
96+
// Navigation
97+
<nav aria-label="breadcrumb">
98+
99+
// Current page in breadcrumb
100+
<span role="link" aria-disabled="true" aria-current="page">{children}</span>
101+
102+
// Decorative separators
103+
<li role="presentation" aria-hidden="true">
104+
105+
// Alerts
106+
<div role="alert">
107+
```
108+
109+
## Focus Styling
110+
111+
All focusable elements must use the project's focus-visible ring pattern:
112+
113+
```
114+
focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:border-ring
115+
```
116+
117+
Do not remove `outline-none` without replacing it with a visible focus indicator.
118+
119+
## UI Primitives A11y
120+
121+
The project's primitives already accept `AriaAttributes`:
122+
123+
- `BlockStack` and `InlineStack` accept all ARIA props
124+
- `Text` accepts `role` and ARIA props
125+
- `Button` has built-in focus-visible and disabled states
126+
- `Input` supports `aria-invalid`, `onEnter`, `onEscape`
127+
- All Radix-based components (Dialog, Select, Checkbox, Switch, Tabs) handle focus trapping and keyboard navigation automatically
128+
129+
## React Flow Considerations
130+
131+
The pipeline editor has specific a11y needs:
132+
133+
- **Handles**: Support click selection and Escape to deselect
134+
- **Task nodes**: Labels are interactive with visual feedback
135+
- **Folders in sidebar**: Use `role="button"` with `aria-expanded`
136+
- **Canvas keyboard shortcuts**: Must not interfere with dialog/modal focus
137+
138+
When adding new interactive elements to the flow canvas, ensure they:
139+
1. Are keyboard accessible
140+
2. Have appropriate ARIA labels
141+
3. Don't capture keyboard events that should reach parent containers
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
---
2+
name: e2e-testing
3+
description: Playwright E2E testing best practices for this project. Use when writing, modifying, or reviewing E2E tests.
4+
---
5+
6+
# E2E Testing Best Practices (Playwright)
7+
8+
## Setup
9+
10+
- Use Playwright helpers from `tests/e2e/helpers.ts`
11+
- Use `data-testid` attributes for stable selectors
12+
- Write descriptive test names that explain user behavior
13+
- Ensure tests are isolated and can run independently
14+
15+
## Never Use Hard-Coded Timeouts
16+
17+
```typescript
18+
// Bad
19+
await element.click();
20+
await page.waitForTimeout(200);
21+
22+
// Good
23+
await element.click();
24+
await expect(otherElement).toBeVisible();
25+
```
26+
27+
## Use Playwright's Auto-Waiting
28+
29+
```typescript
30+
// Bad
31+
if (await element.isVisible()) {
32+
await element.click();
33+
}
34+
35+
// Good
36+
await expect(element).toBeVisible();
37+
await element.click();
38+
```
39+
40+
## Never Use Non-Null Assertions
41+
42+
```typescript
43+
// Bad
44+
const box = await element.boundingBox();
45+
const x = box!.x;
46+
47+
// Good
48+
const box = await element.boundingBox();
49+
if (!box) {
50+
throw new Error("Unable to locate element bounding box");
51+
}
52+
const x = box.x;
53+
```
54+
55+
## Don't Await Locators (They're Lazy)
56+
57+
```typescript
58+
// Bad
59+
const button = await page.getByTestId("submit");
60+
61+
// Good
62+
const button = page.getByTestId("submit");
63+
await expect(button).toBeVisible();
64+
```
65+
66+
## Use Consistent Assertion Patterns
67+
68+
```typescript
69+
// Bad
70+
expect(await element).toHaveText("text");
71+
expect(await element.isVisible()).toBe(true);
72+
73+
// Good
74+
await expect(element).toHaveText("text");
75+
await expect(element).toBeVisible();
76+
```
77+
78+
## Prefer Semantic Selectors
79+
80+
Priority order:
81+
1. `getByRole()` - Best for accessibility
82+
2. `getByTestId()` - Best for test stability (preferred for this app)
83+
3. `getByText()` - Good for static content
84+
4. `locator()` with data attributes - When above don't work
85+
5. CSS selectors - Last resort
86+
87+
## Test Isolation
88+
89+
- Each test should set up its own state
90+
- Don't depend on test execution order (unless using serial mode intentionally)
91+
- Clean up after tests in `afterEach` or `afterAll`
92+
93+
## Helper Functions
94+
95+
- Leverage existing helpers from `tests/e2e/helpers.ts`
96+
- Create new helpers for repeated workflows
97+
- Keep helpers focused and reusable
98+
99+
## Add Meaningful Error Context
100+
101+
```typescript
102+
// Okay
103+
await expect(element).toBeVisible();
104+
105+
// Better
106+
await expect(element, "Component should appear after loading").toBeVisible();
107+
```
108+
109+
## Test User Behavior, Not Implementation
110+
111+
```typescript
112+
// Bad - implementation detail
113+
await expect(button).toHaveClass("bg-blue-500");
114+
115+
// Good - user-visible behavior
116+
await expect(button).toBeVisible();
117+
await expect(button).toBeEnabled();
118+
await expect(button).toHaveText("Submit");
119+
```
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
name: project-conventions
3+
description: Project conventions for Tangle-UI including file structure, imports, code quality, and general rules. Use when writing new code, creating files, or organizing imports.
4+
---
5+
6+
# Project Conventions
7+
8+
## Project Overview
9+
10+
React + TypeScript application for building and running ML pipelines using drag and drop. Uses Vite, TailwindCSS v4, ShadCN, Radix UI, React Flow, and Monaco Editor.
11+
12+
## File Structure & Imports
13+
14+
- Use absolute imports with `@/` prefix for src directory
15+
- Follow existing folder structure:
16+
- `src/components/` for all React components
17+
- `src/hooks/` for custom hooks
18+
- `src/types/` for TypeScript definitions
19+
- `src/utils/` for utility functions
20+
- `src/services/` for API and business logic
21+
- **Import order**: external packages -> internal modules -> relative imports
22+
- Use simple-import-sort rules (already configured in ESLint)
23+
- Do not use barrel exports
24+
25+
## Code Quality
26+
27+
- Follow ESLint rules (configured in eslint.config.js)
28+
- Use Prettier for formatting
29+
- Write tests using Vitest for unit tests, Playwright for E2E
30+
- Use descriptive variable and function names
31+
- Add JSDoc comments for complex functions
32+
- Prefer early returns to reduce nesting
33+
34+
## Comments & Documentation
35+
36+
- Use JSDoc for public APIs
37+
- Add comments for complex business logic
38+
- **Explain "why" not "what" in comments**
39+
- Keep comments up to date with code changes
40+
- Avoid writing redundant comments for functions and variables that are self-explanatory
41+
42+
## Error Handling
43+
44+
- Use proper error boundaries
45+
- Handle async errors with try/catch
46+
- Use toast notifications for user-facing errors
47+
- Log errors appropriately
48+
49+
## React Flow Specific
50+
51+
- Use `@xyflow/react` for flow diagrams
52+
- Follow existing node types and edge patterns
53+
- Keep flow state management consistent with existing patterns
54+
- Use proper node and edge typing
55+
56+
## Specific Project Patterns
57+
58+
- Use Monaco Editor for code editing features
59+
- Use localforage for client-side storage
60+
- Follow existing authentication patterns
61+
- Use proper task node and pipeline handling patterns
62+
- Follow the existing component library structure
63+
- **Do not modify componentSpec structure** without express permission
64+
65+
## Don't Do
66+
67+
- Don't use CSS-in-JS or styled-components
68+
- Don't use inline styling (`style={styles}`) except where strictly necessary
69+
- Don't use relative imports for `@/components/ui`
70+
- Don't create new global state without good reason
71+
- Don't bypass existing abstractions without discussion
72+
73+
## Planning & Documentation
74+
75+
When asked to create planning documents, architecture decisions, or investigation notes:
76+
- **Always save to `.local/`** - This directory is gitignored for local-only files
77+
- Use descriptive filenames: `.local/feature-name-planning.md`, `.local/bug-investigation.md`
78+
79+
## Optional "While We're Here" Cleanup
80+
81+
After completing a code generation task, scan the surrounding area for small, low-risk improvements. **Only offer** if ALL conditions are met:
82+
83+
1. **Small scope**: Affects < 30 lines of code
84+
2. **Low risk**: Purely cosmetic or minor refactoring (not logic changes)
85+
3. **Same file**: In a file you already modified
86+
4. **Clear benefit**: Improves readability, removes dead code, or fixes obvious issues
87+
88+
Don't be naggy — only offer once per task, and only if genuinely worthwhile.

0 commit comments

Comments
 (0)