From efddb808a25edde8f74b62b59779762c78f64dec Mon Sep 17 00:00:00 2001 From: Ethan Graf Date: Wed, 13 May 2026 21:46:47 -0400 Subject: [PATCH] Add design skill --- .gitignore | 3 + .opencode/skills/README.md | 11 +++ .opencode/skills/textile-design/SKILL.md | 77 +++++++++++++++ .opencode/skills/textile-design/a11y.md | 53 +++++++++++ .opencode/skills/textile-design/components.md | 72 ++++++++++++++ .opencode/skills/textile-design/motion.md | 42 +++++++++ .opencode/skills/textile-design/tokens.md | 94 +++++++++++++++++++ 7 files changed, 352 insertions(+) create mode 100644 .opencode/skills/README.md create mode 100644 .opencode/skills/textile-design/SKILL.md create mode 100644 .opencode/skills/textile-design/a11y.md create mode 100644 .opencode/skills/textile-design/components.md create mode 100644 .opencode/skills/textile-design/motion.md create mode 100644 .opencode/skills/textile-design/tokens.md diff --git a/.gitignore b/.gitignore index eae9b77..a5208bf 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,6 @@ out/ .vscode dist + +# Cursor skill symlink (canonical skill lives under .opencode/skills/) +.cursor/skills/textile-design diff --git a/.opencode/skills/README.md b/.opencode/skills/README.md new file mode 100644 index 0000000..e5b7b78 --- /dev/null +++ b/.opencode/skills/README.md @@ -0,0 +1,11 @@ +# Agent skills (OpenCode) + +Skills live in subfolders with `SKILL.md` (see [OpenCode skills docs](https://opencode.ai/docs/skills)). + +**Cursor:** after clone, link the design skill so Cursor discovers it: + +```bash +mkdir -p .cursor/skills && ln -sf ../../.opencode/skills/textile-design .cursor/skills/textile-design +``` + +Run from the repository root (`textile/`). diff --git a/.opencode/skills/textile-design/SKILL.md b/.opencode/skills/textile-design/SKILL.md new file mode 100644 index 0000000..358574b --- /dev/null +++ b/.opencode/skills/textile-design/SKILL.md @@ -0,0 +1,77 @@ +--- +name: textile-design +description: Apply Textile's design system when building or modifying UI in the Textile Electron app. Covers calm-but-alive aesthetic, semantic theming via CSS variables, radius/spacing/motion tokens, accessibility for Electron + macOS, and canonical component patterns. Use when adding, editing, or reviewing any React component, page, modal, or styling in textile/. +--- + +# Textile design + +## Philosophy + +Textile is a **calm, lived-in surface**: low noise, generous rhythm, and motion that reassures without demanding attention. The product tenets in `PRODUCT.md` apply directly—**frictionless access**, **editor-grade performance**, and **the user should live inside Textile**. UI work should feel like a premium home base, not a flashy landing page. + +This skill intentionally **differs** from maximalist “frontend-design” prompts: avoid novelty for its own sake, loud palettes, and busy animation. **Refinement and consistency** carry the premium feel. + +## Before you ship UI + +Use this checklist on every new or changed surface: + +- [ ] **Colors**: only semantic tokens (`bg-background`, `text-foreground`, `border-border`, `bg-primary`, etc.)—no raw hex/oklch in JSX or one-off colors unless adding a new token in `src/index.css`. +- [ ] **Radius**: pick the tier from [tokens.md](tokens.md) (`rounded` / `rounded-md` / `rounded-lg` / `rounded-full`) and stay consistent within one surface (e.g. do not mix `rounded` and `rounded-md` on sibling list rows). +- [ ] **Spacing**: icon `size-4`; icon+label `gap-2`; compact chrome `p-3`; roomy panels `p-5`; list rows `px-2 py-1.5` unless a denser pattern is documented. +- [ ] **Focus**: interactive controls get a visible `focus-visible` ring (see [components.md](components.md)); never `outline-none` without a replacement. +- [ ] **Motion**: prefer CSS transitions on a small set of properties; honor `prefers-reduced-motion` (see [motion.md](motion.md)). +- [ ] **Accessibility**: labels for icon-only controls, correct roles for dialogs/switches/lists, hit targets ≥ 44×44px where practical—see [a11y.md](a11y.md). + +## Theming model + +1. **Theme source**: `:root` and `:root[data-theme='dark']` define `--semantic-*` variables in `src/index.css`. +2. **Tailwind bridge**: `@theme` maps those variables to utilities (`background`, `foreground`, `primary`, `accent`, `border`, `destructive`). +3. **Components**: consume **only** utilities derived from tokens (including opacity modifiers like `text-foreground/60`). Future user themes swap the CSS variables; JSX should not assume fixed colors. + +When adding a new conceptual color (e.g. “warning”), add `--semantic-*` in both light and dark blocks, wire it in `@theme`, then use the new utility—do not spread ad-hoc OKLCH across components. + +Planned token extensions (`--radius-*`, `--motion-*`, `--font-*`, surface/muted) are specified in [tokens.md](tokens.md) for a follow-up CSS PR; until then, use the Tailwind class conventions listed there. + +## Token cheat sheet + +| Area | Rule | +|------------|------| +| Page shell | `bg-background text-foreground` | +| Hairlines | `border-border` (1px borders) | +| Primary CTA | `bg-primary text-primary-foreground` | +| Links / key accents in prose | `text-accent` (per `EditPad`) | +| Danger | `text-destructive` for copy; full destructive buttons TBD | +| Muted copy | `text-foreground/60`–`/80` until `--semantic-muted-fg` lands | + +Full tables and proposed CSS variables: [tokens.md](tokens.md). + +## Motion + +Default global transitions (~320ms) already apply to background, color, border, fill, stroke, shadow. Layer **short** transitions for layout chrome (e.g. sidebar width ~200ms). Details and anti-patterns: [motion.md](motion.md). + +## Accessibility + +Electron uses Chromium: treat the renderer like a **desktop web app** for a11y (keyboard, focus, ARIA, contrast). macOS VoiceOver is the primary screen reader to verify. Electron- and title-bar-specific notes: [a11y.md](a11y.md). + +## Component patterns + +Canonical snippets and file references: [components.md](components.md). + +## Anti-patterns + +- Hard-coded colors (`#fff`, `oklch(...)`) in class strings or inline styles for product chrome. +- `transition-all` or animating `width`/`height` on heavy subtrees without a performance pass. +- Icon-only buttons without `aria-label` (decorative icons: `aria-hidden`). +- Removing outlines without an equivalent `focus-visible` style. +- Inconsistent radius or gap within one list or one modal. + +## Cursor setup + +The committed skill path is `.opencode/skills/textile-design/`. For Cursor, create a symlink (ignored by git): see [../README.md](../README.md). + +## Additional resources + +- [tokens.md](tokens.md) — semantic colors, proposed radii/motion/fonts, spacing vocabulary. +- [motion.md](motion.md) — durations, easings, reduced motion. +- [a11y.md](a11y.md) — Electron, custom title bar, ARIA recipes, VoiceOver smoke test. +- [components.md](components.md) — patterns and migration notes. diff --git a/.opencode/skills/textile-design/a11y.md b/.opencode/skills/textile-design/a11y.md new file mode 100644 index 0000000..739ee5c --- /dev/null +++ b/.opencode/skills/textile-design/a11y.md @@ -0,0 +1,53 @@ +# Accessibility (Textile, Electron renderer) + +## Mental model + +The renderer is **Chromium**. Follow the same baseline as a SPA: + +- Full **keyboard** operation for chrome, modals, and settings. +- Visible **focus** for every focusable control. +- Correct **roles** and **ARIA** for dialogs, switches, lists, and tabs (when introduced). +- **Color contrast** for text and icons against `background` / surfaces (WCAG 2.2 AA: 4.5:1 normal text, 3:1 large text/UI graphics where applicable). + +Screen reader testing: **VoiceOver on macOS** (primary platform today). + +## Electron-specific + +- **Window Controls Overlay / custom title bar:** any interactive control in the drag region must use `WebkitAppRegion: 'no-drag'` so clicks reach the button (see `AppTitleBar`). Do not place the only copy of an action inside a pure drag region. +- **Native menus and accelerators:** when features move to the OS menu, keep **accessible names** in the renderer in sync so VoiceOver and keyboard users are not worse off than menu-only users. +- **Modal dialogs:** backdrop + panel should trap focus in the dialog while open, restore focus on close, and block inert content (future hardening—today verify tab order manually). + +## Patterns in the codebase + +| Pattern | Expectation | +|---------|-------------| +| **Dialog** | `role="dialog"`, `aria-modal="true"`, `aria-label` or labelled-by; Esc to close when appropriate. | +| **Icon-only button** | `aria-label` on `