agentleFS
a-novel-kita-novel-kit/stack/.agents/skills/write-frontend/SKILL.md

write-frontend

Skill2 starsChanged 4 days agoMITView on GitHub

Apply browser conventions to HTML, CSS, TypeScript, platform apps, uikit, Storybook, and browser libraries. Load for browser work; add framework, architecture, and test skills as needed.

The file

Show its rules and text

We split this skill into the rules it gives an agent

46 rules. About 0 are common, 30 are distinctive, and 16 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈30

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 16

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Rendered-UI hard gate: Start Storybook with BROWSER=none and --no-open, inspect the exact changed story in the integrated browser, keep the …

    line 30
  2. Distinctive:

    Use this authority order when guidance conflicts:

    line 36
  3. Distinctive:

    Read references/standards.md before choosing a browser API, accessibility pattern, security boundary, design-token model, or unfamiliar …

    line 44
  4. Very specific:

    Add only tests needed to validate the draft.

    line 51
  5. Distinctive:

    Use repository scripts as declared for final validation.

    line 55
  6. Very specific:

    Run the narrowest package or test target while iterating, then run all applicable gates before the change is ready.

    line 64
  7. Distinctive:

    Treat Storybook as a mandatory review surface for every change that affects rendered UI.

    line 70
  8. Distinctive:

    Add or update the smallest story or docs page that renders the changed UI and its meaningful states.

    line 75
  9. Very specific:

    Start the repository's Storybook command with BROWSER=none and --no-open.

    line 78
  10. Distinctive:

    Wait for readiness, discover the actual listening URL and port, and verify that the exact changed story or docs route responds from where …

    line 80
  11. Distinctive:

    Keep localhost and other local-only Storybook links out of GitHub PR descriptions.

    line 85
  12. Distinctive:

    Prefer the web platform, an existing workspace package, or an existing dependency in that order.

    line 107
  13. Very specific:

    Before implementing reusable code in a terminal app, route visual contracts to uikit and framework-agnostic runtime or shared …

    line 108
  14. Distinctive:

    Prefer standards-body or established-organization ownership.

    line 112
  15. Distinctive:

    Import the narrowest documented public entry point.

    line 114
  16. Distinctive:

    Keep browser bundles free of Node-only modules and server-only transitive dependencies.

    line 115
  17. Very specific:

    Keep strict enabled. Do not introduce any, disable strict checks, or hide errors with broad casts.

    line 119
  18. Very specific:

    Prefer inference inside a function and explicit types at exported, callback, network, storage, and component boundaries.

    line 123
  19. Very specific:

    Use type imports for type-only dependencies and ES modules exclusively.

    line 125
  20. Distinctive:

    Keep domain data serializable unless the boundary explicitly supports richer values.

    line 126
  21. Distinctive:

    Treat API, URL, storage, message, and DOM data as untrusted at runtime even when TypeScript types it.

    line 127
  22. Very specific:

    Keep a declaration out of the statement it feeds.

    line 133
  23. Very specific:

    Put meaningful rendered state and its transition rules in a pure controller with no DOM access, component rendering, route imports, …

    line 160
  24. Distinctive:

    Keep controllers mockable and composable through structural contracts: inject collaborators, compose child controllers, and derive views of …

    line 169
  25. Very specific:

    Use the native element with the required behavior: button for actions, a for navigation, labels and controls for forms, and …

    line 182
  26. Distinctive:

    Add ARIA only when native HTML cannot express the contract.

    line 184
  27. Distinctive:

    Treat persistent state as stronger than transient input.

    line 190
  28. Distinctive:

    Treat WCAG 2.2 AA as the minimum acceptance baseline, not a guarantee supplied by automation.

    line 199
  29. Distinctive:

    Prefer normal flow, Grid, Flexbox, logical properties, and container/media queries over measured JavaScript layout.

    line 213
  30. Distinctive:

    Start from the smallest supported viewport and let content determine breakpoints.

    line 215
  31. Distinctive:

    Use design tokens for product colors, spacing, type, radii, borders, elevation, and motion.

    line 217
  32. Distinctive:

    Use relative units for type and layout.

    line 219
  33. Very specific:

    Keep selectors shallow and component-scoped.

    line 221
  34. Distinctive:

    Put shareable navigation state in the URL.

    line 231
  35. Distinctive:

    Keep rendering pure. Synchronize with external systems in framework lifecycle primitives, not in getters, templates, or module import side …

    line 236
  36. Distinctive:

    Keep server-only code, environment variables, credentials, and privileged API calls out of browser bundles.

    line 238
  37. Distinctive:

    Use progressive enhancement for navigation and forms when the framework supports it.

    line 240
  38. Very specific:

    Use the formal address form for static text when a language distinguishes registers (vous in French, usted in Spanish).

    line 245
  39. Very specific:

    Put no space before a colon in French (Fin prévue:), against standard French typography: a product ruling, enforced by uikit's catalog test.

    line 247
  40. Distinctive:

    Never list the options a reader can see, change or pick, as in "your username and picture": name the whole, as "this information".

    line 251
  41. Distinctive:

    Never place secrets or long-lived credentials in client code, browser storage, logs, analytics, or error messages.

    line 256
  42. Very specific:

    Do not render untrusted HTML.

    line 258
  43. Distinctive:

    Use safe URL construction and validate protocols before navigation.

    line 260
  44. Distinctive:

    Prefer semantic markup and CSS over JavaScript.

    line 271
  45. Very specific:

    Create Intl formatters, RegExp objects, and collators once per locale or module, never per call, item, or render.

    line 288
  46. Distinctive:

    Keep server module state immutable or bounded.

    line 290

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Frontend Conventions (common)

Load develop-feature for local drafting, developer discussion, publication timing, test completion, and final cleanup. Run the affected app locally during a draft as well as Storybook for rendered review; a component library can use its workbench. Explain the flow, expected states, provisional behavior, and boundaries in language the developer can assess.

Apply this base layer to every browser-facing change. Read the target file, its nearest siblings, the package manifest, TypeScript config, lint config, and public exports before editing. Preserve a coherent local pattern unless it conflicts with a rule below or a current platform standard.

This skill owns generic implementation quality, not repository placement. In the A-Novel workspace, use write-platform for terminal application shells and product policy, write-design-system for uikit visual contracts, and nodelib for reusable non-visual client runtime or tooling configuration. Load write-svelte for Svelte files, write-frontend-tests for tests or stories, and write-design-system for tokens or reusable UI. Service REST clients under pkg/js also load write-js-package.

Load plan-ui-design before deciding a new or materially changed flow, interaction pattern, information hierarchy, component family, or visual language. This skill owns implementation quality; plan-ui-design owns the human-facing contract that implementation must preserve.

Rendered-UI hard gate: Start Storybook with BROWSER=none and --no-open, inspect the exact changed story in the integrated browser, keep the server live through review, and put its freshly verified actual Markdown link in the completion report beside the PR and task or issue links. Send screenshots of the changed screens in the conversation as well. Never put a local-only Storybook URL in a GitHub PR body. A screenshot or placeholder never substitutes for the live link.

Use this authority order when guidance conflicts:

  1. Repository contracts and supported-browser policy.
  2. Normative web standards and WCAG.
  3. Official framework or tool documentation for the installed version.
  4. Maintained FOSS guidance from standards bodies and established organizations.
  5. Local convention and personal preference.

Read references/standards.md before choosing a browser API, accessibility pattern, security boundary, design-token model, or unfamiliar framework feature. Verify versioned APIs against current official documentation rather than relying on memory.

Validation by stage

During drafting and issue review, run the app, inspect changed stories, and use focused checks. Add only tests needed to validate the draft. After issue scope approval, complete relevant coverage with write-frontend-tests, including affected Playwright journeys and screenshot comparisons. Perform the final cleanup and rerun affected checks before marking ready.

Use repository scripts as declared for final validation; do not invent parallel commands:

pnpm format                         # write formatting when the repo provides it
pnpm lint                           # formatting check + types + ESLint
a-novel test --type=pnpm -y         # all pnpm tests discovered by the workspace CLI
a-novel build --type=pnpm -y        # production/package builds

Run the narrowest package or test target while iterating, then run all applicable gates before the change is ready. Load use-a-novel-cli whenever running test or build commands. A production build is mandatory for routing, SSR, package-export, bundler, or environment-boundary changes.

Live UI review — mandatory handoff

Treat Storybook as a mandatory review surface for every change that affects rendered UI. Do not declare UI work complete from source inspection, unit tests, screenshots, or a static build alone.

The handoff contract is non-negotiable:

  1. Add or update the smallest story or docs page that renders the changed UI and its meaningful states. If the repository has no usable Storybook, establish it within the agreed scope or report the missing review surface as a blocker.
  2. Start the repository's Storybook command with BROWSER=none and --no-open. The process must not launch an external browser tab and must stay live for the operator unless they ask to stop it.
  3. Wait for readiness, discover the actual listening URL and port, and verify that the exact changed story or docs route responds from where the developer opens it. Behind WSL or another VM boundary, bind IPv4 with --host 0.0.0.0: an IPv6-only listener is not forwarded to the host's 127.0.0.1.
  4. Open that route in the integrated browser and inspect it. Prefer it over the Storybook root when handing off a specific component.
  5. Keep localhost and other local-only Storybook links out of GitHub PR descriptions. They are session-scoped review surfaces, not durable PR metadata.
  6. In each draft or final handoff of rendered UI, include direct app and Storybook routes as applicable, with steps to try, expected results, and limits. Include PR and issue links once they exist; creating them is not a prerequisite to local review. Provide at least one direct Storybook link. Use a clickable inline link such as [Button — Storybook](http://127.0.0.1:6006/?path=/docs/button--docs), substituting the actual live URL and route. Repeat it in any later report that hands completed UI work back; an earlier report does not satisfy the current handoff.
  7. Send screenshots of every new or changed screen and state in the conversation, with the host's file-sharing tool, at the reviewed viewports and in each supported locale. The developer may be unable to reach the local server, from another device or across a VM's network boundary, so the screenshots are their first look. Send them again when the screens change.
  8. Re-resolve every linked route immediately before sending the completion report. If the server stopped or changed, restart it and verify the new link before reporting completion.

Screenshots accompany the live link; a screenshot, placeholder such as “visual preview,” stale URL, or instruction to find an earlier link does not replace it.

Dependencies

  • Load choose-dependency before adding, replacing, or evaluating a package.
  • Prefer the web platform, an existing workspace package, or an existing dependency in that order.
  • Before implementing reusable code in a terminal app, route visual contracts to uikit and framework-agnostic runtime or shared build/lint/test/localization configuration to nodelib.
  • Require an explicit reason for every runtime dependency: capability, maintenance owner, release health, browser cost, license, security posture, and why the current stack cannot provide it.
  • Prefer standards-body or established-organization ownership. Do not adopt an unmaintained package or a narrow personal utility for code that is straightforward to own.
  • Import the narrowest documented public entry point. Never deep-import private package files.
  • Keep browser bundles free of Node-only modules and server-only transitive dependencies.

TypeScript and modules

  • Keep strict enabled. Do not introduce any, disable strict checks, or hide errors with broad casts. Use unknown, narrow it, and validate untrusted data at the boundary.
  • Model impossible states out with discriminated unions. Represent absence deliberately; do not use empty strings, magic numbers, or non-null assertions as state management.
  • Prefer inference inside a function and explicit types at exported, callback, network, storage, and component boundaries. Use satisfies when checking a value without widening it.
  • Use type imports for type-only dependencies and ES modules exclusively.
  • Keep domain data serializable unless the boundary explicitly supports richer values.
  • Treat API, URL, storage, message, and DOM data as untrusted at runtime even when TypeScript types it.
  • Catch unknown; preserve the original cause when translating errors. Never silently discard a rejected promise.
  • Arrays and objects travel by reference, and sort, reverse, splice and fill mutate in place. On data the function does not own, use toSorted, toReversed, toSpliced and with, and copy with structuredClone, never a JSON.parse(JSON.stringify(…)) round trip.
  • Keep a declaration out of the statement it feeds. A long literal inside an if, switch, loop header or call argument becomes a named constant declared just before the statement. A configuration object passed to its factory, as in defineConfig({…}), keeps its idiomatic form.
  • Pipes are idiomatic, but a callback longer than three lines passed to map, filter, reduce, flatMap, forEach, find, some, every, sort, then, catch or a similar method becomes a named function. Declare it outside the enclosing function and pass what it uses as parameters, which makes its captures explicit.

Naming and files

  • Name Svelte components in PascalCase and plain TypeScript/CSS modules in lowercase camelCase or a single lowercase word, matching the surrounding package.
  • Mirror source names in tests and stories: Button.svelte.test.ts, Button.stories.svelte, and retry.test.ts. Follow SvelteKit route filenames exactly where its router owns the convention.
  • Name exported types and components in PascalCase; functions, values, and props in camelCase; and true constants in SCREAMING_SNAKE_CASE only when the package already uses that distinction.
  • Give booleans a state or capability name (disabled, hasError, canRetry) and event callbacks the behavior they represent (onSubmit, onDismiss). Avoid generic data, item, or handler when a domain name is available.

Components and controllers

  • UI components are pure presentation. Reactivity that changes rendered state belongs to an injected controller; components must not create hidden state owners or mutate controller state. Render semantic HTML and accessibility state, translate native events into semantic requests, and own only DOM mechanics such as element references, focus movement, measurements, and transient typeahead bookkeeping.
  • Put meaningful rendered state and its transition rules in a pure controller with no DOM access, component rendering, route imports, storage, session, or network calls. A controller may use the framework reactive primitive in a .svelte.ts module.
  • Let a stateful component accept at most one controller. Do not split its contract across bindable state props, change callbacks, and a controller; stateless components need no controller.
  • Expose semantic controller methods (open, close, select, setChecked) rather than generic setters. The component reports intent; the controller may accept, transform, or reject it.
  • Export the controller contract and a configurable default factory. Callers may supply another implementation that satisfies the same contract, including fixed-state Storybook controllers.
  • Keep controllers mockable and composable through structural contracts: inject collaborators, compose child controllers, and derive views of shared state without creating a second state owner. A plain typed object with fixed state and no-op transitions must be a valid replacement.
  • Build every rendered state from static values. With fixed props and a fixed-state controller, a story reaches each state of the matrix, edge cases included, with no timer, fetch, play function or user event. Only DOM mechanics the user drives stay outside, such as focus, the caret, typing in progress and scroll. A state a story cannot reach that way is hidden state: move it into the controller.
  • Unit-test controller transitions without rendering. Test the component boundary for DOM semantics, accessibility behavior, and rejected transitions.

HTML and interaction

  • Use the native element with the required behavior: button for actions, a for navigation, labels and controls for forms, and landmarks/headings for document structure.
  • Add ARIA only when native HTML cannot express the contract. A role is a promise to implement its keyboard interaction, focus behavior, state, and accessible name.
  • Set type="button" on non-submit buttons. Keep form submission, validation, autofill, and error association available without pointer input.
  • Preserve logical source order and normal tab order. Never use a positive tabindex.
  • Expose a visible focus indicator. Do not remove outlines without an equal or stronger replacement.
  • Treat persistent state as stronger than transient input. Selected, checked, expanded, invalid, loading, and disabled meaning must remain clear while a control is hovered, active, or focused.
  • For reusable content-bearing components, prefer the framework's native composition primitive over a string-only label prop when text, markup, icons, or nested components are semantically safe.
  • Support keyboard, pointer, touch, zoom, reflow, text spacing, and assistive technology. Do not make color, hover, drag, or animation the only way to understand or operate a control.
  • Provide useful alternative text and accessible names. Decorative media stays silent.
  • Set document and changed-passage language correctly. Use logical CSS properties so RTL does not require a second component implementation.
  • Treat WCAG 2.2 AA as the minimum acceptance baseline, not a guarantee supplied by automation.

CSS and responsive layout

  • Build coherence into the code, not into a sequence of visual corrections. Equivalent UI roles and states must reuse the same shared components, variants, tokens, and constants across screens and breakpoints. When they drift, trace the owning styles and remove duplicated definitions or compensating overrides before adding new CSS. Fix reusable behavior in uikit; keep only genuine application composition and layout differences local. Do not recreate a shared element just to tailor its appearance, or invent an abstraction for unrelated roles that merely look similar.
  • Responsive rules normally change geometry, not a control's visual identity: preserve shared foreground, surface, opacity, typography, and interaction-state rules unless the product contract explicitly requires a difference. Verify the shared implementation on each actual backdrop; matching token names alone do not prove matching rendered colors through transparency layers.
  • Prefer normal flow, Grid, Flexbox, logical properties, and container/media queries over measured JavaScript layout. Use feature queries for optional enhancements.
  • Start from the smallest supported viewport and let content determine breakpoints. Avoid device-name breakpoints and user-agent sniffing.
  • Use design tokens for product colors, spacing, type, radii, borders, elevation, and motion. A raw value is acceptable only for a local algorithmic constant with no design meaning.
  • Use relative units for type and layout. Keep line height unitless. Reserve pixels for genuinely device-bound details such as a one-device-pixel hairline when the token contract calls for it.
  • Keep selectors shallow and component-scoped. Avoid !important; fix cascade ownership instead.
  • Preserve content at 200% text zoom and 400% page zoom/reflow. Do not clip user content to force a mockup height.
  • Respect prefers-reduced-motion, forced colors, increased contrast where supported, and user font settings. Motion must not be required to understand state.
  • Animate compositor-friendly properties when possible and never add animation without a purpose.

State, data, and browser boundaries

  • Give each piece of state one owner. Derive values instead of synchronizing duplicate state.
  • Put shareable navigation state in the URL. Keep ephemeral interaction state local to the smallest component that owns it.
  • Represent loading, empty, error, stale, and success states explicitly. Preserve useful content during refresh when the product contract allows it.
  • Cancel or supersede obsolete asynchronous work. Guard against out-of-order responses.
  • Keep rendering pure. Synchronize with external systems in framework lifecycle primitives, not in getters, templates, or module import side effects.
  • Keep server-only code, environment variables, credentials, and privileged API calls out of browser bundles. Assume every shipped byte and source map is public.
  • Use progressive enhancement for navigation and forms when the framework supports it. A network or JavaScript failure should degrade intentionally rather than strand the user.

Localization language register

  • Use the formal address form for static text when a language distinguishes registers (vous in French, usted in Spanish).
  • Use the language’s conventional action-label form for buttons, links, and other controls. A control whose action is about the reader (signing up, signing in or out, their account and its details) speaks in the reader’s voice: M’inscrire, Changer mon mot de passe. A control that works the interface keeps the plain form, even on the reader’s data: Afficher le mot de passe. Other text names the reader’s things as theirs and says who acts, never an impersonal Impossible de….
  • Put no space before a colon in French (Fin prévue:), against standard French typography: a product ruling, enforced by uikit's catalog test. It applies to every catalog, page title and test expectation.
  • Apply the same distinction to accessible names and Storybook states.
  • Never list the options a reader can see, change or pick, as in "your username and picture": name the whole, as "this information". A new option then needs no copy change, and nothing goes stale.

Security and privacy

  • Never place secrets or long-lived credentials in client code, browser storage, logs, analytics, or error messages. Follow the repository authentication model.
  • Do not render untrusted HTML. Use text interpolation; if rich HTML is a real product requirement, choose and configure a maintained sanitizer through choose-dependency and test bypass cases.
  • Use safe URL construction and validate protocols before navigation. Do not concatenate executable markup, CSS, script, or query fragments from untrusted input.
  • Collect and persist only required data. Do not add analytics, remote fonts, third-party scripts, beacons, or cross-origin calls without explicit product and security approval.
  • Preserve CSP compatibility: avoid inline script generation, eval, and undocumented third-party origins.

Performance and compatibility

  • Set an explicit browser baseline in project configuration. Use feature detection and progressive enhancement; add a polyfill only after measuring need and bundle cost.
  • Prefer semantic markup and CSS over JavaScript. Lazy-load non-critical routes and heavy features, not primary content or interaction affordances.
  • Reserve media dimensions, serve responsive assets, and avoid layout thrashing. Batch DOM reads and writes only after measurement proves imperative layout is necessary.
  • Measure before optimizing. Track user-facing latency and Core Web Vitals for applications; do not trade correctness or accessibility for an unmeasured micro-optimization.
  • Handle offline, timeout, retry, and partial-response behavior at the owning boundary. Never retry a non-idempotent operation invisibly.
  • Apply the shared traps in prefer-small-solutions. JavaScript adds these, in the browser and on the server:
    • Deep recursion throws RangeError and keeps every enclosing closure alive. Walk trees shaped by data with an explicit stack.
    • An async function that awaits a call to itself, such as a polling return poll(), chains one pending promise per turn and keeps them all. Loop with while and await instead.
    • Give every addEventListener, setInterval, observer, and subscription a teardown at the same ownership level. Prefer passing an AbortSignal as { signal } so one abort removes them all.
    • Create Intl formatters, RegExp objects, and collators once per locale or module, never per call, item, or render.
    • Keep server module state immutable or bounded. Every SSR request shares it, so a per-user entry is a leak and a privacy defect.
    • On the server, read or cancel every fetch response body, error statuses included: await response.body?.cancel() before an early return or throw. Node's undici returns the connection to its pool only once the body settles.

Completion checklist

  • Semantic and keyboard behavior works without a mouse.
  • Focus, zoom/reflow, reduced motion, forced colors, long content, localization, and RTL were considered in proportion to the change.
  • Persistent states remain stable under transient interactions, and reusable content APIs compose naturally without sacrificing native semantics.
  • Trust boundaries validate runtime data and do not expose secrets.
  • Loading, empty, error, and success paths are intentional.
  • write-frontend-tests covers changed behavior.
  • Storybook is running, the changed UI was inspected there, and a freshly verified direct local link is in the completion report beside the PR and task or issue links, never in the PR body.
  • Format, lint, pnpm tests, and production build pass.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.