5.2 KiB
5.2 KiB
name, description
| name | description |
|---|---|
| semantics-frontend | Core protocol for Svelte 5 (Runes) Components, UX State Machines, and Visual-Interactive Validation. |
[DEF:Std:Semantics:Frontend]
@COMPLEXITY: 5
@PURPOSE: Canonical GRACE-Poly protocol for Svelte 5 (Runes) Components, UX State Machines, and Project UI Architecture.
@RELATION: DEPENDS_ON ->[Std:Semantics:Core]
@INVARIANT: Frontend components MUST be verifiable by an automated GUI Judge Agent (e.g., Playwright).
@INVARIANT: Use Tailwind CSS exclusively. Native fetch is forbidden.
0. SVELTE 5 PARADIGM & UX PHILOSOPHY
- STRICT RUNES ONLY: You MUST use Svelte 5 Runes for reactivity:
$state(),$derived(),$effect(),$props(),$bindable(). - FORBIDDEN SYNTAX: Do NOT use
export let,on:event(useonclick), or the legacy$:reactivity. - UX AS A STATE MACHINE: Every component is a Finite State Machine (FSM). You MUST declare its visual states in the contract BEFORE writing implementation.
- RESOURCE-CENTRIC: Navigation and actions revolve around Resources. Every action MUST be traceable.
I. PROJECT ARCHITECTURAL INVARIANTS
You are bound by strict repository-level design rules. Violating these causes instant PR rejection.
- Styling: Tailwind CSS utility classes are MANDATORY. Minimize scoped
<style>. If custom CSS is absolutely necessary, use@applydirectives. - Localization: All user-facing text MUST use the
$tstore fromsrc/lib/i18n. No hardcoded UI strings. - API Layer: You MUST use the internal
requestApi/fetchApiwrappers. Using nativefetch()is a fatal violation.
II. UX CONTRACTS (STRICT UI BEHAVIOR)
Every component MUST define its behavioral contract in the header.
@UX_STATE:Maps FSM state names to visual behavior. Example:@UX_STATE: Loading -> Spinner visible, btn disabled, aria-busy=true.@UX_FEEDBACK:Defines external system reactions (Toast, Shake, RedBorder).@UX_RECOVERY:Defines the user's recovery path from errors (e.g.,Retry button,Clear Input).@UX_REACTIVITY:Explicitly declares the state source. Example:@UX_REACTIVITY: Props -> $props(), LocalState -> $state(...).@UX_TEST:Defines the interaction scenario for the automated Judge Agent. Example:@UX_TEST: Idle -> {click: submit, expected: Loading}.
III. STATE MANAGEMENT & STORE TOPOLOGY
- Subscription: Use the
$prefix for reactive store access (e.g.,$sidebarStore). - Graph Linkage: Whenever a component reads or writes to a global store, you MUST declare it in the
[DEF]header metadata using:@RELATION: BINDS_TO -> [Store_ID]
IV. IMPLEMENTATION & ACCESSIBILITY (A11Y)
- Event Handling: Use native attributes (e.g.,
onclick={handler}). - Transitions: Use Svelte's built-in transitions for UI state changes to ensure smooth UX.
- Async Logic: Any async task (API calls) MUST be handled within a
try/catchblock that explicitly triggers an@UX_STATEtransition toErroron failure and provides@UX_FEEDBACK(e.g., Toast). - A11Y: Ensure proper ARIA roles (
aria-busy,aria-invalid) and keyboard navigation. Use semantic HTML (<nav>,<main>).
V. LOGGING (MOLECULAR TOPOLOGY FOR UI)
Frontend logging bridges the gap between your logic and the Judge Agent's vision system.
- [EXPLORE]: Log branching user paths or caught UI errors.
- [REASON]: Log the intent before an API invocation.
- [REFLECT]: Log visual state updates (e.g., "Toast displayed", "Drawer opened").
- Syntax:
console.info("[ComponentID][MARKER] Message", {extra_data})— Prefix MUST be manually applied.
VI. CANONICAL SVELTE 5 COMPONENT TEMPLATE
You MUST strictly adhere to this AST boundary format:
<!-- [DEF:ComponentName:Component] -->
<script>
/**
* @COMPLEXITY: [1-5]
* @PURPOSE: Brief description of the component purpose.
* @LAYER: UI
* @SEMANTICS: list, of, keywords
* @RELATION: DEPENDS_ON -> [OtherComponent]
* @RELATION: BINDS_TO -> [GlobalStore]
*
* @UX_STATE: Idle -> Default view.
* @UX_STATE: Loading -> Button disabled, spinner active.
* @UX_FEEDBACK: Toast notification on success/error.
* @UX_REACTIVITY: Props -> $props(), State -> $state().
* @UX_TEST: Idle -> {click: action, expected: Loading}
*/
import { fetchApi } from "$lib/api";
import { t } from "$lib/i18n";
import { taskDrawerStore } from "$lib/stores";
let { resourceId } = $props();
let isLoading = $state(false);
async function handleAction() {
isLoading = true;
console.info("[ComponentName][REASON] Opening task drawer for resource", { resourceId });
try {
taskDrawerStore.open(resourceId);
await fetchApi(`/api/resource/${resourceId}/process`);
console.info("[ComponentName][REFLECT] Process completed successfully");
} catch (e) {
console.error("[ComponentName][EXPLORE] Action failed", { error: e });
} finally {
isLoading = false;
}
}
</script>
<div class="flex flex-col p-4 bg-white rounded-lg shadow-md">
<button
class="btn-primary"
onclick={handleAction}
disabled={isLoading}
aria-busy={isLoading}
>
{#if isLoading} <span class="spinner"></span> {/if}
{$t('actions.start')}
</button>
</div>
<!--[/DEF:ComponentName:Component] -->