Frontend Development — ayunis-core-frontend
SkillMediaFrontend development in ayunis-core. Use when creating, modifying, or debugging frontend code (React, Feature-Sliced Design, API client).
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Frontend Development — ayunis-core-frontend skill
What this skill tells your AI
The instructions your AI receives, as published by ayunis-core/ayunis-core in .claude/skills/ayunis-core-frontend-dev/SKILL.md and read by ahel’s review.
Working Directory
All commands run from ayunis-core-frontend/:
cd ayunis-core-frontend
Before modifying any layer, read its SUMMARY.md in src/[layer]/SUMMARY.md. The top-level src/SUMMARY.md provides an overview.
Validation Sequence
Choose validation breadth using the repository's Proportional Workflow.
For Standard and High-Risk changes:
pnpm run build # Must succeed
pnpm run lint # Must pass
For Fast Path changes, run the narrowest relevant test when behavior or test code changed, plus any lint/type check applicable to the touched files. Run the build when imports, types, dependencies, bundling, or other compile-time behavior could be affected.
Browser journey or system boundary changed? Load the e2e skill when
lower-level tests do not sufficiently prove the behavior. Add data-testids
needed by the journey to touched components in the same PR
(<feature>-<element>, kebab-case) — text selectors are banned because the UI
is i18n'd.
Would visual evidence materially help review? After the product PR exists,
load the pr-media skill and publish the smallest useful scene set to
pr-media/pr-<number>. Do not commit media scenes to the product branch.
Architecture (Feature-Sliced Design)
packages/ui/ # Framework-level UI primitives, tokens, and utilities
ayunis-core-frontend/src/
├── pages/ # Route components (compose widgets/features)
├── widgets/ # Reusable composites (used in ≥2 pages)
├── features/ # Self-contained business logic
└── shared/ # App-wide infrastructure and app-aware UI
Import rules: pages → widgets → features → shared
Layers only depend on layers to their right. Never import upward.
Check demonstrated conventions before deciding placement
The FSD rules above are the theory; this repo's actual conventions are the tie-breaker. Before deciding where a slice or component lives — feature vs widget vs page, which page a route maps to, whether something is "shared enough" to promote a layer — grep how comparable cases are already structured and follow that, rather than reasoning from FSD principles alone.
- The abstract heuristics ("used in ≥2 pages → widget", "used from more than one slice → promote") are necessary but not sufficient. They routinely disagree with how the codebase actually draws its boundaries when viewed across the whole repo instead of a single branch.
- Concrete convention that has bitten before: pages map one slice per route — e.g. a list route and its detail route are separate page slices (
skills.indexvsskill.$id), not one page reused across two routes. Check the route files andsrc/pages/before assuming a shared placement. - When a colocated placement (feature/page) and a "promote to shared/widget" placement both look defensible, the existing convention wins. Look at the
Reference Pagesin thenew-pageskill and grep sibling slices before moving code. - Do not double down on a theory-driven placement after pushback — re-check the convention first. See "Confirm placement before mutating a stacked PR chain" below.
Confirm placement before mutating a stacked PR chain
Do not execute structural moves and amend commits across a stacked-PR chain (e.g. gt modify a parent, then check out and amend the child) off a preliminary placement conclusion. Settle the placement against repo conventions first, then move — reverting a wrong move across stacked branches means restoring exact pre-session SHAs from the reflog.
Shared UI Package — Registry-Managed Primitives
Framework-level primitives live in the repository-root packages/ui/ workspace and are imported through @ayunis/ui subpaths. App-aware shared components remain in ayunis-core-frontend/src/shared/ui/.
- Run shadcn registry commands from
packages/ui/, whosecomponents.jsonowns the aliases and registry configuration:cd ../packages/ui && pnpm dlx shadcn@latest add <component>. - Do not patch
packages/ui/src/components/for a feature-specific use case. Wrap or compose the primitive in the relevant frontendui/directory orsrc/shared/ui/instead. - Changes inside
packages/ui/must remain application-independent and be intentional design-system work. If a primitive genuinely needs a new generic capability, confirm that scope with the user before changing it. - Every newly introduced
packages/ui/src/components/<name>.tsxcomponent must include a colocated<name>.stories.tsx. Cover its representative default state and meaningful generic variants, states, or interactions; compound components should be demonstrated as a usable composition. - Import primitives from their public subpaths, such as
@ayunis/ui/components/button, andcnfrom@ayunis/ui/lib/cn. Do not reach intopackages/ui/src/from the frontend.
Reach for existing primitives and tokens
Compose the existing design system before hand-rolling layout, and use design tokens instead of raw Tailwind color scales:
- Look for a composite primitive first. For an icon-plus-label row (banners, result cards, list rows) use the
Itemfamily —Item/ItemMedia variant="icon"/ItemContent— rather than assembling a bareflexcontainer yourself. Check@ayunis/uifor a framework primitive andsrc/shared/ui/for an app-aware component before adding your own. - Use semantic color tokens, never hardcoded palette classes.
text-brand,text-muted-foreground, etc. — nottext-amber-500,text-blue-600, or other raw Tailwind color scales, which break theming and dark mode.
Page module internals
Each page module can have these subdirectories:
src/pages/<page-name>/
├── ui/ # Components — state, hooks, JSX only
├── api/ # Mutation hooks (one per operation)
├── model/ # Types, constants, schemas
└── lib/ # Pure helper functions (formatting, URL building, data transforms)
Keep ui/ components focused on component logic. Extract pure functions that don't depend on React state or hooks into lib/.
API Client
After backend API changes, regenerate the client:
pnpm run openapi:update # Regenerates src/shared/api/generated/
Never edit generated code manually — it will be overwritten.
Hook Pattern
One hook per operation, encapsulating mutation logic. Use showSuccess/showError from @/shared/lib/toast for user feedback, and extractErrorData from @/shared/api/extract-error-data for structured error handling.
For hooks that back a form (create/update dialogs), load the frontend-form-pattern skill — it covers form types, structure, and the full end-to-end validation pattern including backend DTO validation, field-level error display, and i18n.
Verifying in the Browser
When the change affects rendered UI or browser behavior, use your harness's browser tooling to check the affected page renders and the console is clean. A render failure shows the React dev-server error overlay — the element #webpack-dev-server-client-overlay must not exist. The frontend URL depends on the dev slot (see dev-environment); seeded login credentials are in seed-database.
Completion Checklist
- Validation matches the repository's Proportional Workflow
- Relevant focused tests pass when behavior or test code changed
- Build and package lint pass for Standard and High-Risk changes
- Affected page renders without console errors when UI or browser behavior changed
- No
anytypes introduced - Import rules respected (no upward imports)
- UI primitives use public
@ayunis/uisubpaths - New
packages/uicomponents include representative Storybook stories - No feature-specific behavior added to
packages/ui/
Signals
- GitHub stars
- 33
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
ayunis-core-frontend-dev- Source
- github.com/ayunis-core/ayunis-core