From 5621da27f575f4650f17bf452935a054713e1c16 Mon Sep 17 00:00:00 2001 From: albx Date: Sat, 30 May 2026 16:58:04 +0200 Subject: [PATCH 1/9] #86 - add copilot instructions for blazor and a11y development --- .github/instructions/a11y.instructions.md | 748 ++++++++++++++++++++ .github/instructions/blazor.instructions.md | 158 +++++ 2 files changed, 906 insertions(+) create mode 100644 .github/instructions/a11y.instructions.md create mode 100644 .github/instructions/blazor.instructions.md diff --git a/.github/instructions/a11y.instructions.md b/.github/instructions/a11y.instructions.md new file mode 100644 index 0000000..d0079e8 --- /dev/null +++ b/.github/instructions/a11y.instructions.md @@ -0,0 +1,748 @@ +--- +applyTo: '**' +description: 'Comprehensive web accessibility standards based on WCAG 2.2 AA, with 38+ anti-patterns, legal enforcement context (EAA, ADA Title II), WAI-ARIA patterns, and Blazor-specific fixes for UI kit component authoring.' +--- + +# Accessibility Standards + +Comprehensive accessibility rules for web application development. Every anti-pattern includes a severity classification, detection method, WCAG 2.2 reference, and corrective code examples. + +**Severity levels:** + +- **CRITICAL** — Users cannot access content at all. Must be fixed before merge. +- **IMPORTANT** — Significant barrier for assistive technology users. Fix in same sprint. +- **SUGGESTION** — Improves usability for assistive technology. Plan for a future iteration. + +--- + +## WCAG 2.2 Quick Reference (AA Level) + +### Perceivable + +| Criterion | Level | Summary | +|-----------|-------|---------| +| 1.1.1 Non-text Content | A | All non-text content has a text alternative. Decorative images use `alt=""`. | +| 1.2.1 Audio/Video-only | A | Provide transcript (audio) or text alternative (video). | +| 1.2.2 Captions (Prerecorded) | A | All prerecorded video has synchronized captions. | +| 1.3.1 Info and Relationships | A | Structure (headings, lists, tables, labels, landmarks) programmatically conveyed. | +| 1.3.2 Meaningful Sequence | A | When the sequence that content is presented affects its meaning, the visual and programmatic ordering of content should align. | +| 1.3.3 Sensory Characteristics | A | Instructions don't rely solely on shape, size, position, or sound. | +| 1.3.4 Orientation | AA | Content is not restricted to single orientation unless essential. | +| 1.3.5 Identify Input Purpose | AA | Input fields have `autocomplete` attributes when collecting information about the user. | +| 1.4.1 Use of Color | A | Color is not the only means of conveying info. | +| 1.4.3 Contrast (Minimum) | AA | Text: 4.5:1 normal, 3:1 large (18pt / 14pt bold). | +| 1.4.4 Resize Text | AA | Text resizable to 200% without loss of content or functionality. | +| 1.4.10 Reflow | AA | Sections of content can fit within 320px CSS width viewports without needing to scroll in two dimensions to read. | +| 1.4.11 Non-text Contrast | AA | UI components and graphics: 3:1 against adjacent colors. | +| 1.4.12 Text Spacing | AA | No loss of content or functionality with user-overridden line-height (1.5x), or specified paragraph spacing, letter spacing, and word spacing adjustments. | +| 1.4.13 Content on Hover/Focus | AA | Popup content that appears on hover or focus is: dismissible, hoverable, persistent. | + +### Operable + +| Criterion | Level | Summary | +|-----------|-------|---------| +| 2.1.1 Keyboard | A | All functionality operable via keyboard. | +| 2.1.2 No Keyboard Trap | A | User can navigate away from any component using keyboard. | +| 2.2.1 Timing Adjustable | A | Time limits can be extended or disabled. | +| 2.2.2 Pause, Stop, Hide | A | Auto-updating content can be paused. | +| 2.3.1 Three Flashes | A | No content flashes more than 3 times per second. | +| 2.4.1 Bypass Blocks | A | Skip link to bypass repeated navigation. | +| 2.4.2 Page Titled | A | Pages have descriptive ``. | +| 2.4.3 Focus Order | A | Focus order preserves meaning and operability. | +| 2.4.4 Link Purpose | A | Link purpose determinable from text or context. | +| 2.4.6 Headings and Labels | AA | Headings and labels describe topic or purpose. | +| 2.4.7 Focus Visible | AA | Keyboard focus indicator is visible. | +| 2.4.11 Focus Not Obscured | AA | Focused element not entirely hidden by other overlaying elements (such as sticky headers or footers). *(New in 2.2)* | +| 2.5.1 Pointer Gestures | A | Multi-point gestures have single-pointer alternative. | +| 2.5.2 Pointer Cancellation | A | Activation on up-event, unless activation can be aborted, reversed, or down-event activation is essential. | +| 2.5.3 Label in Name | A | Accessible name contains the label text as it is visually presented. | +| 2.5.4 Motion Actuation | A | Device motion has UI alternative and can be disabled. | +| 2.5.7 Dragging Movements | AA | Drag-and-drop has click/tap alternative. *(New in 2.2)* | +| 2.5.8 Target Size (Minimum) | AA | Interactive controls have a target size, or spacing of at least 24x24 CSS px. *(New in 2.2)* | + +### Understandable + +| Criterion | Level | Summary | +|-----------|-------|---------| +| 3.1.1 Language of Page | A | `<html lang="...">` set correctly. | +| 3.1.2 Language of Parts | AA | Content in different language marked with `lang` attribute. | +| 3.2.1 On Focus | A | Focus doesn't trigger unexpected context change. | +| 3.2.2 On Input | A | Changing input doesn't auto-trigger unexpected context change. | +| 3.2.6 Consistent Help | A | Help mechanisms in same relative order across pages. *(New in 2.2)* | +| 3.3.1 Error Identification | A | Errors described to user in text. | +| 3.3.2 Labels or Instructions | A | Labels or instructions provided for user input. | +| 3.3.3 Error Suggestion | AA | Suggest corrections for detected errors. | +| 3.3.4 Error Prevention | AA | Submissions are reversible, checked, or confirmed. | +| 3.3.7 Redundant Entry | A | Don't re-ask for info already provided in same process. *(New in 2.2)* | +| 3.3.8 Accessible Authentication (Minimum) | AA | No cognitive function test (puzzle CAPTCHA). Allow paste and autofill. *(New in 2.2)* | + +### Robust + +| Criterion | Level | Summary | +|-----------|-------|---------| +| 4.1.2 Name, Role, Value | A | All UI components have accessible name, role, and state. | +| 4.1.3 Status Messages | AA | Status messages announced by screen readers without receiving focus. | + +> **Note:** 4.1.1 Parsing is obsolete in WCAG 2.2 (always satisfied). Issues it covered are now addressed by 1.3.1 and 4.1.2. + +> **New AAA criteria in 2.2** (not required for AA, but recommended): 2.4.12 Focus Not Obscured (Enhanced), 2.4.13 Focus Appearance, 3.3.9 Accessible Authentication (Enhanced). + +> **Looking ahead:** WCAG 3.0 (W3C Accessibility Guidelines) is in Working Draft (March 2026). It replaces pass/fail with Bronze/Silver/Gold conformance and "Outcomes" instead of "Success Criteria." It is NOT yet a standard — continue targeting WCAG 2.2 AA. + +## Legal Enforcement Context (2026) + +- **European Accessibility Act (EAA)**: Enforced since June 2025 across all 27 EU member states. Applies to digital products and services. Fines up to EUR 3 million. References EN 301 549 (maps to WCAG 2.1 AA). +- **ADA Title II (US)**: Digital accessibility rule effective April 2026 for state/local governments serving 50,000+ people (April 2027 for smaller entities). Requires WCAG 2.1 AA. +- **Section 508 (US Federal)**: References WCAG 2.0 AA (refresh to 2.1/2.2 expected). + +**Target**: WCAG 2.2 AA covers all current legal requirements (superset of 2.1 AA and 2.0 AA). + +--- + +## Five Rules of ARIA + +1. **Prefer native HTML** — Use `<button>` not `<div role="button">`. Native elements have built-in keyboard, focus, and semantics. +2. **Don't change native semantics where prohibited** — Don't add `role="heading"` to a `<button>`. Use the correct element. +3. **All ARIA controls must be keyboard operable** — If `role="button"`, handle Enter and Space key events. +4. **Don't use `aria-hidden="true"` on focusable elements** — Hidden from assistive tech but still focusable creates a "ghost" element. +5. **All interactive elements need an accessible name** — Via label, `aria-label`, `aria-labelledby`, or visible text content. + +--- + +## Semantic HTML Anti-Patterns (S1-S8) + +### S1: Missing `lang` Attribute on `<html>` + +- **Severity**: CRITICAL +- **Detection**: `<html` without `lang=` +- **WCAG**: 3.1.1 (A) + +```html +<!-- BAD --> +<html> + +<!-- GOOD --> +<html lang="en"> +``` + +Blazor Server / WASM: Set in `wwwroot/index.html`. Blazor SSR: Set in `App.razor` or `Routes.razor` on the root `<html>` element. + +### S2: Multiple `<h1>` Per Page + +- **Severity**: SUGGESTION +- **Detection**: Multiple `<h1>` elements that make the page heading structure unclear +- **WCAG**: Best practice (supports 1.3.1) + +Prefer one `<h1>` per page representing the main topic. Use `<h2>` for sections. Multiple `<h1>` elements are not a strict WCAG violation but can confuse screen reader navigation. + +### S3: Heading Level Gaps + +- **Severity**: IMPORTANT +- **Detection**: `<h1>` followed by `<h3>` (skipping `<h2>`) +- **WCAG**: 1.3.1 (A) + +Maintain logical nesting: `h1 > h2 > h3 > h4`. Style headings with CSS, not by choosing a different heading level. + +### S4: Div Soup — No Landmark Elements + +- **Severity**: IMPORTANT +- **Detection**: Pages using only `<div>` without `<nav>`, `<main>`, `<header>`, `<footer>` +- **WCAG**: Best practice (supports 1.3.1, 2.4.1) + +```html +<!-- GOOD --> +<header>...</header> +<nav aria-label="Main">...</nav> +<main>...</main> +<footer>...</footer> +``` + +### S5: Layout Tables Without `role="presentation"` + +- **Severity**: IMPORTANT +- **Detection**: `<table>` without `<th>`, `<caption>`, or `role="presentation"` +- **WCAG**: 1.3.1 (A) + +Use CSS Grid/Flexbox for layout. If table must be used for layout, add `role="presentation"`. + +### S6: Data Tables Without Headers + +- **Severity**: CRITICAL +- **Detection**: `<table>` with data rows but no `<th>` elements +- **WCAG**: 1.3.1 (A) + +```html +<!-- GOOD --> +<table> + <caption>User list</caption> + <thead> + <tr><th scope="col">Name</th><th scope="col">Email</th></tr> + </thead> + <tbody> + <tr><td>Alice</td><td>alice@example.com</td></tr> + </tbody> +</table> +``` + +### S7: Non-Descriptive Link Text + +- **Severity**: IMPORTANT +- **Detection**: `>click here<|>read more<|>learn more<|>here<|>more<|>link<` +- **WCAG**: 2.4.4 (A) + +```html +<!-- BAD --> +<a href="/pricing">Click here</a> + +<!-- GOOD --> +<a href="/pricing">View pricing plans</a> +``` + +### S8: Interactive Elements Without Semantic HTML + +- **Severity**: CRITICAL +- **Detection**: `<div.*(?:@onclick|onClick|@click|\(click\))` +- **WCAG**: 4.1.2 (A) + +```razor +@* BAD — not focusable, no role, no keyboard support *@ +<div @onclick="HandleClick">Submit</div> + +@* GOOD *@ +<button @onclick="HandleClick">Submit</button> +``` + +--- + +## ARIA Anti-Patterns (A1-A8) + +### A1: Redundant ARIA on Native Elements + +- **Severity**: SUGGESTION +- **Detection**: `<button.*role="button"|<nav.*role="navigation"|<a.*role="link"` +- **WCAG**: ARIA Rule 2 + +Remove redundant ARIA. `<button>` already has `role="button"`. + +### A2: `aria-hidden="true"` on Focusable Element + +- **Severity**: CRITICAL +- **Detection**: `aria-hidden="true"` on focusable elements (button, input, a, [tabindex]) +- **WCAG**: ARIA Rule 4 + +Do not leave focusable elements inside `aria-hidden="true"` content or use `aria-hidden="true"` on focusable elements. Rather, for native controls that support it, such as `<button>` or `<input>`, use `disabled` if disabling the control is the intended behavior. For `<a>` elements or arbitrary elements made focusable with `[tabindex]`, remove focusability instead (for example, remove `href` or `tabindex`). If the content should be completely non-interactive and hidden from assistive technology, use `inert`, `hidden`, or remove it from the DOM. + +### A3: Missing Required ARIA Properties + +- **Severity**: CRITICAL +- **Detection**: `role="tab"` without `aria-selected`, `role="checkbox"` without `aria-checked` +- **WCAG**: 4.1.2 (A) + +Required per role: `tab` needs `aria-selected`; `combobox` needs `aria-expanded`/`aria-controls`; `slider` needs `aria-valuemin`/`aria-valuemax`/`aria-valuenow`; `checkbox` needs `aria-checked`. + +### A4: Invalid ARIA Role Values + +- **Severity**: CRITICAL +- **Detection**: `role="[^"]*"` with non-existent values +- **WCAG**: 4.1.2 (A) + +Invalid roles are ignored by assistive technology. Common mistakes: `role="input"`, `role="text"`, misspellings. + +### A5: ARIA Where Native HTML Works + +- **Severity**: IMPORTANT +- **Detection**: `role="button"` on `<div>`, `role="checkbox"` on `<div>` +- **WCAG**: ARIA Rule 1 + +```html +<!-- BAD — requires manual keyboard, focus, and state management --> +<div role="checkbox" aria-checked="false" tabindex="0">Accept terms</div> + +<!-- GOOD — all behavior built-in --> +<label><input type="checkbox" /> Accept terms</label> +``` + +### A6: Missing `aria-label` on Icon-Only Buttons + +- **Severity**: CRITICAL +- **Detection**: `<button` with SVG/icon child and no text or `aria-label` +- **WCAG**: 4.1.2 (A) + +```html +<!-- GOOD --> +<button aria-label="Close dialog"><svg aria-hidden="true">...</svg></button> +``` + +### A7: `role="presentation"` on Focusable Elements + +- **Severity**: IMPORTANT +- **Detection**: `role="presentation"` on interactive elements +- **WCAG**: ARIA Rule 4 + +Browsers will ignore the presentation role on focusable elements. + +### A8: Missing Live Region for Dynamic Content + +- **Severity**: IMPORTANT +- **Detection**: Toast/notification components without `role="alert"`, `role="status"`, or `aria-live` +- **WCAG**: 4.1.3 (AA) + +```html +<!-- GOOD — content announced when content is injected into a preexisting live region element in the DOM --> +<div role="status" aria-live="polite">Item saved successfully</div> +<!-- Use role="alert" (assertive) for errors --> +<div role="alert">Failed to save. Please try again.</div> +``` + +--- + +## Keyboard and Focus Anti-Patterns (K1-K7) + +### K1: `onClick` Without `onKeyDown` on Non-Native Elements + +- **Severity**: CRITICAL +- **Detection**: `(?:@onclick|onClick|@click|\(click\))` on `<div>` or `<span>` without keyboard handler +- **WCAG**: 2.1.1 (A) + +Use `<button>` instead. In Blazor, if a div is required: add `role="button"`, `tabindex="0"`, and handle `@onkeydown` for Enter and Space activation. + +### K2: Positive `tabindex` Values + +- **Severity**: CRITICAL +- **Detection**: `(?:tabindex="[1-9]\d*"|tabIndex=\{[1-9]\d*\})` +- **WCAG**: 2.4.3 (A) + +Only use `tabindex="0"` (add to tab order) and `tabindex="-1"` (programmatic focus only). + +### K3: Focus Trap Without Escape + +- **Severity**: CRITICAL +- **Detection**: Modal/overlay without Escape key handler or focus trapping +- **WCAG**: 2.1.2 (A) + +Use native `<dialog>` with `showModal()` — it prevents keyboard focus from moving to the inert non-dialog content. Additionally, it has built in Escape key to dismiss, and focus will automatically return to the invoking element (if available). If a custom modal dialog implementation is needed: trap Tab within the dialog or use the `inert` attribute for non-dialog content (do not use `inert` on an element that contains the dialog), dismiss on Escape (unless user confirmation of an action is essential), return focus to the trigger element on close, or to best logical location if triggering element is no longer present upon dismissal. + +### K4: Missing Skip Link + +- **Severity**: IMPORTANT +- **Detection**: No skip link as first focusable element +- **WCAG**: 2.4.1 (A) + +```html +<a href="#main-content" class="skip-link">Skip to main content</a> +<nav>...</nav> +<main id="main-content" tabindex="-1">...</main> +``` + +```css +.skip-link { position: absolute; top: -40px; left: 0; padding: 8px 16px; background: #000; color: #fff; z-index: 100; } +.skip-link:focus { top: 0; } +``` + +### K5: `outline: none` Without Replacement + +- **Severity**: CRITICAL +- **Detection**: `outline:\s*none|outline:\s*0\b` without `:focus-visible` replacement +- **WCAG**: 2.4.7 (AA) + +```css +/* GOOD */ +button:focus-visible { outline: 2px solid #005fcc; outline-offset: 2px; } +``` + +### K6: Mouse-Only Interactions + +- **Severity**: IMPORTANT +- **Detection**: `onMouseOver|onMouseEnter|@mouseenter` without keyboard equivalent +- **WCAG**: 2.1.1 (A) + +Pair hover with focus events. Use `onFocus`/`onBlur` alongside `onMouseEnter`/`onMouseLeave`. + +### K7: Focus Not Returned After Custom Modal Close + +- **Severity**: IMPORTANT +- **Detection**: Custom modal dialog close without restoring focus to trigger +- **WCAG**: 2.4.3 (A) + +Store reference to trigger element or to best logical location if triggering element is no longer present upon close. On modal close, call `triggerElement.focus()`. + +--- + +## Form Anti-Patterns (F1-F6) + +### F1: Input Without Associated Label + +- **Severity**: CRITICAL +- **Detection**: `<input|<select|<textarea` without `<label>`, `aria-label`, or `aria-labelledby` +- **WCAG**: 1.3.1 (A), 3.3.2 (A) + +```html +<!-- GOOD --> +<label for="email">Email address</label> +<input id="email" type="email" placeholder="you@example.com" /> +``` + +### F2: Error Messages Not Linked to Input + +- **Severity**: CRITICAL +- **Detection**: Error elements near inputs without `aria-describedby` +- **WCAG**: 3.3.1 (A) + +```html +<input id="email" type="email" aria-describedby="email-error" aria-invalid="true" /> +<span id="email-error" class="error">Invalid email format</span> +``` + +### F3: Required Field Indicated Only by Color or `*` + +- **Severity**: IMPORTANT +- **Detection**: `required` or `*` without `aria-required="true"` or HTML `required` +- **WCAG**: 3.3.2 (A), 1.4.1 (A) + +```html +<label for="name">Name <span aria-hidden="true">*</span></label> +<input id="name" type="text" required /> +<p class="form-note">Fields marked * are required</p> +``` + +### F4: No Error Summary or Focus on First Error + +- **Severity**: IMPORTANT +- **Detection**: Form submit handler without focus management on validation failure +- **WCAG**: 3.3.1 (A) + +On submit failure, focus the first invalid field or show and focus an error summary. + +### F5: CAPTCHA Without Accessible Alternative + +- **Severity**: IMPORTANT +- **Detection**: Puzzle/image CAPTCHAs without fallback; password fields with `autocomplete='off'` or paste-blocking JavaScript +- **WCAG**: 3.3.8 (AA) + +Use reCAPTCHA v3 (invisible), hCaptcha accessibility mode, or alternative authentication. Never block paste or autofill on password fields — this violates WCAG 3.3.8. + +### F6: Placeholder as Label + +- **Severity**: IMPORTANT +- **Detection**: `placeholder=` without accompanying `<label>`, `aria-label`, or `aria-labelledby` +- **WCAG**: 3.3.2 (A) + +Always pair placeholder with a visible `<label>`. Placeholder is a hint, not a label. + +--- + +## Visual and Color Anti-Patterns (V1-V5) + +### V1: Insufficient Text Contrast + +- **Severity**: CRITICAL +- **Detection**: Text color combinations below 4.5:1 (normal) or 3:1 (large) +- **WCAG**: 1.4.3 (AA) + +```css +/* BAD — #999 on #fff is ~2.5:1 */ +.text { color: #999; background: #fff; } + +/* GOOD — #595959 on #fff is 7.0:1 */ +.text { color: #595959; background: #fff; } +``` + +### V2: Information Conveyed by Color Alone + +- **Severity**: CRITICAL +- **Detection**: Error/success states distinguished only by color +- **WCAG**: 1.4.1 (A) + +Add a secondary indicator: icon, text, pattern, underline, or border. + +### V3: Fixed Font Sizes Preventing Resize + +- **Severity**: IMPORTANT +- **Detection**: `font-size:\s*[0-9]*px` (excluding root/base) +- **WCAG**: 1.4.4 (AA) + +Use `rem` or `em` for font sizes. Base font can be `px`, but content fonts should be relative. + +### V4: Content Not Reflowing at 320px + +- **Severity**: IMPORTANT +- **Detection**: Fixed-width containers, horizontal scroll at narrow widths +- **WCAG**: 1.4.10 (AA) + +Use responsive layouts (Grid, Flexbox). Test at 320px CSS width. Avoid fixed-width containers. + +### V5: Animation Without `prefers-reduced-motion` + +- **Severity**: SUGGESTION +- **Detection**: `animation:|transition:` without `prefers-reduced-motion` media query +- **WCAG**: 2.3.3 (AAA) + +Best practice and AAA enhancement. Gate non-essential animations behind `prefers-reduced-motion` so users who request less motion are not forced to experience interaction-triggered effects. + +```css +@media (prefers-reduced-motion: no-preference) { + .card { transition: transform 0.3s ease; } + .card:hover { transform: scale(1.05); } +} +``` + +--- + +## Media Anti-Patterns (D1-D4) + +### D1: Informational Image Without Alt Text + +- **Severity**: CRITICAL +- **Detection**: `<img|<Image` without `alt=` attribute +- **WCAG**: 1.1.1 (A) + +Alt text decision tree: decorative = `alt=""`; contains text = include that text; functional = describe action; informational = describe content. + +### D2: Decorative Image with Non-Empty Alt + +- **Severity**: SUGGESTION +- **Detection**: Decorative images with meaningful alt text +- **WCAG**: 1.1.1 (A) + +Use `alt=""` for decorative images. Add `aria-hidden="true"` for decorative SVGs. + +### D3: Video Without Captions + +- **Severity**: CRITICAL +- **Detection**: `<video` without `<track kind="captions">` +- **WCAG**: 1.2.2 (A) + +```html +<video src="/tutorial.mp4" controls> + <track kind="captions" src="/tutorial-en.vtt" srclang="en" label="English" default /> +</video> +``` + +### D4: Audio/Video Autoplay + +- **Severity**: IMPORTANT +- **Detection**: `autoplay` attribute without `muted` +- **WCAG**: 1.4.2 (A) + +Never autoplay audio. If video autoplays, start muted with controls. + +--- + +## Framework-Specific: Blazor (BL1-BL6) + +### BL1: `@onclick` on Non-Interactive Element Without Keyboard Handler + +- **Severity**: CRITICAL +- **Detection**: `@onclick` on `<div>` or `<span>` without `@onkeydown` +- **WCAG**: 2.1.1 (A), 4.1.2 (A) + +Always use `<button>`. If a non-button element is unavoidable, add `role="button"`, `tabindex="0"`, and handle both Enter and Space via `@onkeydown`: + +```razor +@* BAD *@ +<div @onclick="HandleClick">Submit</div> + +@* GOOD *@ +<button @onclick="HandleClick">Submit</button> +``` + +### BL2: Missing `<PageTitle>` on Routable Pages + +- **Severity**: IMPORTANT +- **Detection**: `@page` directive present but no `<PageTitle>` component in the file +- **WCAG**: 2.4.2 (A) + +Every routable Blazor page must declare a unique `<PageTitle>` so screen readers announce the new page on navigation: + +```razor +@page "/products" +<PageTitle>Products — MyApp</PageTitle> +``` + +### BL3: Route Change Without Live Announcement + +- **Severity**: IMPORTANT +- **Detection**: `NavigationManager.NavigateTo` or `<NavLink>` navigation without a live region reflecting the new page title +- **WCAG**: 4.1.3 (AA) + +Blazor has no built-in route announcer. Add a persistent `role="status"` live region in `MainLayout.razor` and update it on `LocationChanged`: + +```razor +@inject NavigationManager Nav + +<div role="status" aria-live="polite" aria-atomic="true" class="visually-hidden">@_announcement</div> + +@code { + private string _announcement = string.Empty; + + protected override void OnInitialized() + { + Nav.LocationChanged += OnLocationChanged; + } + + private void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + _announcement = document.title; // or read from a shared title service + StateHasChanged(); + } + + public void Dispose() => Nav.LocationChanged -= OnLocationChanged; +} +``` + +### BL4: Modal Close Without Focus Restoration + +- **Severity**: IMPORTANT +- **Detection**: Modal or overlay component that sets `Visible = false` without calling `ElementReference.FocusAsync()` on the trigger +- **WCAG**: 2.4.3 (A) + +Store an `ElementReference` to the trigger button and restore focus on close: + +```razor +<button @ref="_triggerRef" @onclick="OpenModal">Open</button> + +@code { + private ElementReference _triggerRef; + + private async Task CloseModal() + { + IsVisible = false; + await _triggerRef.FocusAsync(); + } +} +``` + +### BL5: `EditForm` Validation Errors Without `aria-invalid` / `aria-describedby` + +- **Severity**: CRITICAL +- **Detection**: `<EditForm>` with `<ValidationMessage>` but the associated input lacks `aria-invalid` and `aria-describedby` +- **WCAG**: 3.3.1 (A), 4.1.2 (A) + +In `BitFormComponentBase<T>` and derived input components, bind validation state to ARIA attributes so screen readers announce errors inline: + +```razor +<label for="@FieldId">@Label</label> +<input id="@FieldId" + aria-invalid="@(IsInvalid ? "true" : null)" + aria-describedby="@(IsInvalid ? $"{FieldId}-error" : null)" /> +<span id="@FieldId-error" role="alert"> + <ValidationMessage For="@ValueExpression" /> +</span> +``` + +### BL6: `@if` Toggle Without Focus Management + +- **Severity**: IMPORTANT +- **Detection**: `@if` conditionally rendering interactive content (panels, drawers, alerts) without moving focus into or away from the revealed element +- **WCAG**: 2.4.3 (A) + +After showing content, use `ElementReference.FocusAsync()` with `Task.Yield()` to allow the DOM to update before focusing: + +```razor +@if (_isOpen) +{ + <div @ref="_panelRef" tabindex="-1">...</div> +} + +@code { + private bool _isOpen; + private ElementReference _panelRef; + + private async Task Open() + { + _isOpen = true; + StateHasChanged(); + await Task.Yield(); // let Blazor flush the DOM update + await _panelRef.FocusAsync(); + } +} +``` + +--- + +## Keyboard Interaction Reference + +| Key | Expected Behavior | +|-----|-------------------| +| `Tab` | Move focus to next focusable element in DOM order | +| `Shift+Tab` | Move focus to previous focusable element | +| `Enter` | Activate buttons and links | +| `Space` | Activate buttons, toggle checkboxes, select radio buttons | +| `Escape` | Close modals, dialogs, popovers, dropdowns | +| `Arrow Up/Down` | Navigate within menus, listboxes, radio groups, tabs | +| `Arrow Left/Right` | Navigate within tab bars, sliders, radio groups | +| `Home` | Move to first item in list, menu, or tab bar | +| `End` | Move to last item in list, menu, or tab bar | + +### Widget-Specific Patterns + +| Widget | Tab enters | Internal nav | Activate | Exit | +|--------|-----------|-------------|----------|------| +| Tab bar | Focus active tab | Arrow Left/Right | automatic or Enter | Tab out | +| Menu | Focus first item | Arrow Up/Down | Enter | Escape | +| Dialog | Focus first element | Tab cycles within | Enter on buttons | Escape | +| Combobox | Focus input | Arrow Up/Down | Enter selects | Escape closes | +| Tree view | Focus first node | Arrow keys | Enter/Space | Tab out | + +--- + +## Color Contrast Quick Reference + +### Text Contrast (WCAG 1.4.3 AA) + +| Text Type | Minimum Ratio | +|-----------|--------------| +| Normal text (< 18pt / < 14pt bold) | 4.5:1 | +| Large text (>= 18pt / >= 14pt bold) | 3:1 | +| Incidental (disabled, decorative) | No requirement | + +### Non-Text Contrast (WCAG 1.4.11 AA) + +| Element | Minimum Ratio | +|---------|--------------| +| UI components (borders, icons) | 3:1 against adjacent | +| Graphical objects | 3:1 against adjacent | +| Focus indicators | 3:1 against background | + +--- + +## Accessibility Checklist (POUR) + +### Perceivable +- [ ] All images have appropriate alt text (descriptive or empty for decorative) +- [ ] Videos have synchronized captions +- [ ] Page uses semantic landmarks: `<header>`, `<nav>`, `<main>`, `<footer>` +- [ ] Headings follow logical hierarchy (h1 > h2 > h3, no gaps) +- [ ] Text contrast meets 4.5:1 (normal) / 3:1 (large) +- [ ] UI component contrast meets 3:1 +- [ ] Information not conveyed by color alone +- [ ] Content reflows at 320px without horizontal scroll +- [ ] `<html lang="...">` is set correctly +- [ ] Text resizable to 200% without loss of content + +### Operable +- [ ] All functionality accessible via keyboard +- [ ] No keyboard traps (Escape closes overlays) +- [ ] Skip link provided as first focusable element +- [ ] Focus indicator visible on all interactive elements +- [ ] Focus order matches visual order +- [ ] Focus not obscured by sticky headers/footers +- [ ] Focus returned to trigger after modal close +- [ ] Touch targets at least 24x24 CSS px +- [ ] Animations respect `prefers-reduced-motion` +- [ ] No content flashes more than 3 times per second + +### Understandable +- [ ] All form inputs have associated `<label>` or `aria-label` +- [ ] Error messages linked to inputs via `aria-describedby` +- [ ] Required fields indicated with `required` or `aria-required` +- [ ] Error summary or focus-on-first-error on submit failure +- [ ] No unexpected context changes on focus or input + +### Robust +- [ ] All interactive elements have accessible name, role, and state +- [ ] ARIA roles have required properties +- [ ] No `aria-hidden="true"` on focusable elements +- [ ] Dynamic content announced via live regions +- [ ] Blazor route changes announced via a `role="status"` live region in `MainLayout.razor` (see BL3) +- [ ] No redundant ARIA on native HTML elements diff --git a/.github/instructions/blazor.instructions.md b/.github/instructions/blazor.instructions.md new file mode 100644 index 0000000..993a1df --- /dev/null +++ b/.github/instructions/blazor.instructions.md @@ -0,0 +1,158 @@ +--- +description: 'Blazor UI kit library component authoring patterns and conventions for BitBlazor' +applyTo: '**/*.razor, **/*.razor.cs, **/*.razor.css' +--- + +## Project Context + +BitBlazor is a UI kit library providing accessible, reusable Blazor components styled with **Bootstrap Italia**. All conventions in this file apply to library component authoring, not application development. + +## Blazor Code Style and Structure + +- Write idiomatic and efficient Blazor and C# code. +- Follow .NET and Blazor conventions. +- Use Razor Components appropriately for component-based UI development. +- Prefer inline `@code` blocks only for trivial rendering helpers; place all non-trivial logic in a `.razor.cs` code-behind partial class. +- Async/await should be used where applicable to ensure non-blocking UI operations. + +## Naming Conventions + +- Follow PascalCase for component names, method names, and public members. +- Use camelCase for private fields and local variables. +- Prefix interface names with "I" (e.g., `IUserService`). +- **All library components must be prefixed with `Bit`** (e.g., `BitButton`, `BitModal`, `BitTextField`). +- Component file names must match the component class name exactly (e.g., `BitButton.razor` + `BitButton.razor.cs`). + +## Component Architecture and Base Classes + +Every library component must inherit from the appropriate base class: + +- **`BitComponentBase`** — base for all non-form components. Provides `CssClass`, `Id`, and `AdditionalAttributes`. +- **`BitFormComponentBase<T>`** — base for form components that bind a value. Provides `Label`, `Value`, `ValueChanged`, `ValueExpression`, and `EditContext` integration. +- **`BitInputFieldBase<T>`** — base for text-like input fields. Extends `BitFormComponentBase<T>` with `Readonly`, `Plaintext`, `Placeholder`, and `Size`. + +Never inherit directly from `ComponentBase` for library components. + +### Partial Class Pattern + +Split every non-trivial component into two files: + +- `BitXxx.razor` — markup and `@code` rendering helpers only. +- `BitXxx.razor.cs` — `partial class` with all `[Parameter]` declarations, private state, and computed properties. + +### Attribute Splatting + +Always forward unknown attributes to the root HTML element via `AdditionalAttributes` (already declared on `BitComponentBase`): + +```razor +<button @attributes="AdditionalAttributes">...</button> +``` + +### Required Parameters + +Mark parameters that have no sensible default as `[EditorRequired]`: + +```csharp +[Parameter] +[EditorRequired] +public RenderFragment ChildContent { get; set; } +``` + +## CSS Class Building + +Use `CssClassBuilder` (from `BitBlazor.Core`) to compose CSS class strings. Never concatenate class strings manually: + +```csharp +// Good +var builder = new CssClassBuilder("btn") + .Add($"btn-{Color.ToCssClass()}") + .Add("btn-sm", Size == Size.Small); +AddCustomCssClass(builder); +return builder.Build(); + +// Bad +string cssClass = "btn btn-" + color + (small ? " btn-sm" : ""); +``` + +Always call `AddCustomCssClass(builder)` before `Build()` to allow consumers to inject custom classes. + +## Design Tokens and Enums + +Use the provided enum types as parameter types instead of raw strings or primitives: + +| Enum | Purpose | +|------|---------| +| `Color` | Color variants (Primary, Secondary, Success, …) | +| `Size` | Size variants (Default, Small, Large) | +| `Variant` | Visual style (Solid, Outline, Ghost, …) | +| `Ratio` | Aspect ratio | +| `Typography` | Typography scale | + +```csharp +// Good +[Parameter] +public Color Color { get; set; } + +// Bad +[Parameter] +public string Color { get; set; } = "primary"; +``` + +## Bootstrap Italia Styling + +Components are styled exclusively with **Bootstrap Italia** CSS classes. Do not introduce custom CSS that duplicates or overrides Bootstrap Italia utilities. Keep component-specific styles in `.razor.css` scoped files. + +## CSS Isolation + +Use `.razor.css` scoped stylesheets for component-specific rules that are not expressible via Bootstrap Italia classes: + +- File must be named `BitXxx.razor.css` alongside `BitXxx.razor`. +- Only put styles here that are truly component-scoped; prefer Bootstrap Italia utility classes wherever possible. + +## XML Documentation Comments + +All public types, properties, and methods in `.razor.cs` files must have `<summary>` XML documentation. The project has `<GenerateDocumentationFile>true</GenerateDocumentationFile>` and documentation is published: + +```csharp +/// <summary> +/// Gets or sets the color style of the button. +/// </summary> +[Parameter] +public Color Color { get; set; } +``` + +## Blazor and .NET Specific Guidelines + +- Utilize Blazor's built-in component lifecycle methods (`OnInitializedAsync`, `OnParametersSetAsync`). +- Use data binding effectively with `@bind`. +- Leverage Dependency Injection for any services needed by components. +- Structure components following Separation of Concerns (markup in `.razor`, logic in `.razor.cs`). +- Target C# 13 features (record types, pattern matching, global usings) as the baseline — the library targets `net9.0`. + +## Error Handling and Validation + +- Capture component-level rendering errors with `ErrorBoundary` where appropriate. +- Implement form validation support via DataAnnotations and `EditContext` integration in `BitFormComponentBase<T>`. + +## Performance Optimization + +- Optimize Razor components by reducing unnecessary renders; use `ShouldRender()` where render frequency must be controlled. +- Minimize the component render tree depth. +- Use `EventCallback` (not `Action` or `Func`) for user interaction callbacks to ensure correct rendering behaviour. + +## Accessibility + +Library components have first-class accessibility responsibilities. Follow the companion `a11y.instructions.md` for full WCAG 2.2 AA rules, and enforce these within every component: + +- Render semantic HTML elements (`<button>`, `<input>`, `<label>`, `<nav>`, etc.) — never `<div>` or `<span>` for interactive roles. +- Always associate `<label>` with its input via `for`/`id` or wrap the input inside the label. +- Icon-only interactive elements must expose an `aria-label`. +- Forward ARIA attributes through `AdditionalAttributes` so consumers can override them. +- Dynamic content changes must use live regions (`role="status"` / `role="alert"`) where appropriate. +- Ensure focus is visible and keyboard-operable for every interactive component. + +## Testing + +- Test components using **bUnit** alongside xUnit. +- Use Moq or NSubstitute for mocking dependencies. +- Cover parameter combinations, CSS class output, event callbacks, and accessibility attributes in tests. From 09d05fd83a92d242f765c6914aa87a063b0479c9 Mon Sep 17 00:00:00 2001 From: albx <albi.mo87@hotmail.it> Date: Sat, 30 May 2026 18:07:43 +0200 Subject: [PATCH 2/9] #86 - add blazor skill for bitblazor development --- .github/skills/blazor/SKILL.md | 145 +++++++++++++++ .github/skills/blazor/manifest.json | 5 + .../skills/blazor/references/anti-patterns.md | 17 ++ .../skills/blazor/references/docs-template.md | 95 ++++++++++ .github/skills/blazor/references/patterns.md | 165 ++++++++++++++++++ .github/skills/blazor/references/stories.md | 76 ++++++++ .github/skills/blazor/references/testing.md | 109 ++++++++++++ 7 files changed, 612 insertions(+) create mode 100644 .github/skills/blazor/SKILL.md create mode 100644 .github/skills/blazor/manifest.json create mode 100644 .github/skills/blazor/references/anti-patterns.md create mode 100644 .github/skills/blazor/references/docs-template.md create mode 100644 .github/skills/blazor/references/patterns.md create mode 100644 .github/skills/blazor/references/stories.md create mode 100644 .github/skills/blazor/references/testing.md diff --git a/.github/skills/blazor/SKILL.md b/.github/skills/blazor/SKILL.md new file mode 100644 index 0000000..1286d49 --- /dev/null +++ b/.github/skills/blazor/SKILL.md @@ -0,0 +1,145 @@ +--- +name: blazor +description: "Author, extend, and review BitBlazor library components — accessible, Bootstrap Italia-styled Blazor UI kit components for .NET 9+. USE FOR: creating a new Bit component, extending an existing component, adding a form field, reviewing component markup or parameters, writing bUnit tests for a component. DO NOT USE FOR: unrelated stacks or building Blazor applications with BitBlazor (not authoring the library itself). INVOKES: inspect the repository context, edit targeted files, and run relevant build and test commands when changes are made." +compatibility: "Requires .NET 9+. Library targets net9.0 and uses Bootstrap Italia for all styling." +--- + +# BitBlazor Component Authoring + +## Trigger On + +- creating a new `Bit` prefixed component (e.g., `BitAlert`, `BitTextField`) +- extending an existing BitBlazor component with new parameters or behaviour +- adding a form field component that integrates with `EditForm` and `EditContext` +- reviewing component markup, parameter declarations, or CSS class composition +- writing or reviewing bUnit tests for a library component +- integrating with JavaScript from a library component + +## Documentation + +- [Blazor Overview](https://learn.microsoft.com/en-us/aspnet/core/blazor/?view=aspnetcore-10.0) +- [Blazor Component Lifecycle](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-10.0) +- [Blazor Performance Best Practices](https://learn.microsoft.com/en-us/aspnet/core/blazor/performance?view=aspnetcore-10.0) +- [Blazor JS Interop](https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/?view=aspnetcore-10.0) +- [Bootstrap Italia](https://italia.github.io/bootstrap-italia/docs) +- [bUnit Testing](https://bunit.dev/docs/getting-started/) + +### References + +- [patterns.md](references/patterns.md) — General-purpose and form component code patterns, CSS class composition, performance, JS interop +- [anti-patterns.md](references/anti-patterns.md) — BitBlazor-specific mistakes and how to avoid them +- [testing.md](references/testing.md) — bUnit testing patterns and conventions +- [stories.md](references/stories.md) — BlazingStory file structure and conventions +- [docs-template.md](references/docs-template.md) — Documentation page template and structure + +## Component Authoring Workflow + +1. **Choose the right base class** (see Base Class Hierarchy below) +2. **Create the two component files** in `src/BitBlazor/Components/<Name>/` or `src/BitBlazor/Form/<Name>/`: + - `BitXxx.razor` — markup only; inline `@code` for trivial render helpers + - `BitXxx.razor.cs` — partial class with all `[Parameter]` declarations, private state, and computed properties +3. **Declare parameters** with XML `<summary>` docs; use enum types (`Color`, `Size`, `Variant`) instead of raw strings +4. **Compose the CSS class string** using `CssClassBuilder`; always call `AddCustomCssClass()` before `Build()` +5. **Write semantic markup** using native HTML elements; forward unknown attributes via `@attributes="AdditionalAttributes"` +6. **Wire ARIA attributes** for interactive and form components (see [Accessibility](#accessibility) section) +7. **Create a test file** in `tests/BitBlazor.Test/Components/<Name>/` or `tests/BitBlazor.Test/Form/<Name>/` — see [testing.md](references/testing.md) +8. **Create a story file** mirroring the component's namespace under `stories/BitBlazor.Stories/Components/Stories/` — see [stories.md](references/stories.md) +9. **Write the documentation page** in `docs/components/` or `docs/form/` — see [docs-template.md](references/docs-template.md) + +## Base Class Hierarchy + +Choose the base class that matches the component's role: + +| Base Class | When to Use | Key Members Provided | +|------------|-------------|----------------------| +| `BitComponentBase` | General-purpose display and interactive components (alert, badge, card, button, …) | `CssClass`, `Id`, `AdditionalAttributes`, `AddCustomCssClass()` | +| `BitFormComponentBase<T>` | Form components that bind a value | Everything above + `Label`, `Value`, `ValueChanged`, `ValueExpression`, `Required`, `Disabled`, `AdditionalText`, `AdditionalTextId`, `CurrentEditContext` | +| `BitInputFieldBase<T>` | Text-like input fields | Everything above + `Readonly`, `Plaintext`, `Placeholder`, `Size` | + +**Never inherit directly from `ComponentBase`.** See [patterns.md](references/patterns.md) for full component examples. + +## CSS Class Composition + +Use `CssClassBuilder` (from `BitBlazor.Core`) to compose CSS class strings — never concatenate manually. Always call `AddCustomCssClass()` before `Build()`: + +```csharp +var builder = new CssClassBuilder("btn") + .Add($"btn-{Color.ToCssClass()}") + .Add("btn-sm", Size == Size.Small); +AddCustomCssClass(builder); +return builder.Build(); +``` + +Use Bootstrap Italia CSS class names exclusively. See [patterns.md](references/patterns.md) for complete examples. + + +## Design Token Enums + +Always use the project's enum types as parameter types — never raw strings: + +| Enum | Values | Example Parameter | +|------|--------|-------------------| +| `Color` | `Primary`, `Secondary`, `Success`, `Warning`, `Danger`, … | `public Color Color { get; set; }` | +| `Size` | `Default`, `Small`, `Large` | `public Size Size { get; set; } = Size.Default;` | +| `Variant` | `Solid`, `Outline`, `Ghost`, … | `public Variant Variant { get; set; } = Variant.Solid;` | +| `Ratio` | Aspect ratio values | `public Ratio Ratio { get; set; }` | +| `Typography` | Typography scale values | `public Typography Typography { get; set; }` | + +## Render Mode Compatibility + +Library components must work in **all render modes** used by the consuming application. Follow these rules: + +| Concern | Rule | +|---------|------| +| JS Interop | Always use `IJSRuntime` async APIs; never synchronous interop | +| State | Components are stateless by design — values flow in via parameters, changes flow out via `EventCallback` | +| No DB / HTTP calls | Library components never access services directly; they are pure UI | +| `EventCallback` | Use `EventCallback` (not `Action`/`Func`) for callbacks to ensure correct rendering | + +## Accessibility + +Accessibility is a **first-class requirement** for every component in this library. The authoritative ruleset is [`a11y.instructions.md`](../../instructions/a11y.instructions.md), which is automatically active on every `.razor`, `.razor.cs`, and `.razor.css` edit — read it before authoring any interactive component. + +The checks most commonly missed when creating a new BitBlazor component are: + +| Check | Rule | +|-------|------| +| **BL1** — `@onclick` on `<div>` or `<span>` | Always use `<button>`. If unavoidable, add `role="button"`, `tabindex="0"`, and handle Enter + Space via `@onkeydown` | +| **BL4** — Modal/overlay close without focus restoration | Store an `ElementReference` to the trigger; call `FocusAsync()` on close | +| **BL5** — `EditForm` inputs missing `aria-invalid` / `aria-describedby` | Wire both attributes from `IsInvalid` — see [patterns.md](references/patterns.md) | +| **BL6** — `@if` revealing interactive content without focus management | After showing content call `FocusAsync()` with `await Task.Yield()` to let Blazor flush the DOM first | + +Additionally, for every component: +- Icon-only interactive elements **must** have `aria-label` +- Never remove `outline` in `.razor.css` without providing a `:focus-visible` replacement +- Colour must not be the **sole** indicator of state (error, success, disabled) + +See [anti-patterns.md](references/anti-patterns.md) for a full list of BitBlazor-specific mistakes to avoid. + +See [patterns.md](references/patterns.md) for performance best practices and JS interop patterns. + +See [stories.md](references/stories.md) for the BlazingStory file template and conventions. + +See [testing.md](references/testing.md) for bUnit test examples and conventions. + +## Deliver + +- `BitXxx.razor` + `BitXxx.razor.cs` inheriting the correct base class +- all parameters documented with XML `<summary>` comments +- CSS classes composed via `CssClassBuilder` with `AddCustomCssClass()` before `Build()` +- semantic HTML with `@attributes="AdditionalAttributes"` on the root element +- ARIA attributes wired correctly for interactive and form components +- a bUnit test file — see [testing.md](references/testing.md) +- a BlazingStory file — see [stories.md](references/stories.md) +- a documentation page — see [docs-template.md](references/docs-template.md) + +## Validate + +- component builds without warnings (`dotnet build`) +- all bUnit tests pass (`dotnet test`) +- component renders correctly with `CssClass` injected by a consumer +- form components announce validation errors via `aria-invalid` and `aria-describedby` +- icon-only interactive elements expose `aria-label` +- no `outline: none` without a `:focus-visible` replacement in `.razor.css` +- all BL1–BL6 checks from `a11y.instructions.md` pass for the new component +- documentation page exists in `docs/` with parameter table and at least one usage example diff --git a/.github/skills/blazor/manifest.json b/.github/skills/blazor/manifest.json new file mode 100644 index 0000000..1ebf70f --- /dev/null +++ b/.github/skills/blazor/manifest.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.0", + "category": "Web", + "package_prefix": "BitBlazor" +} diff --git a/.github/skills/blazor/references/anti-patterns.md b/.github/skills/blazor/references/anti-patterns.md new file mode 100644 index 0000000..cede6e1 --- /dev/null +++ b/.github/skills/blazor/references/anti-patterns.md @@ -0,0 +1,17 @@ +# BitBlazor Anti-Patterns + +| Anti-Pattern | Why It's Bad | Better Approach | +|--------------|--------------|-----------------| +| Inheriting `ComponentBase` directly | Loses `CssClass`, `Id`, `AdditionalAttributes`, `AddCustomCssClass()` | Inherit `BitComponentBase`, `BitFormComponentBase<T>`, or `BitInputFieldBase<T>` | +| Manual CSS string concatenation | Fragile, duplicates classes, no deduplication | Use `CssClassBuilder` | +| Raw `string` for color/size parameters | No type safety, breaks design tokens | Use `Color`, `Size`, `Variant` enums | +| `<div>` or `<span>` for interactive roles | Not keyboard operable, no built-in semantics | Use native `<button>`, `<input>`, `<a>` | +| Forgetting `AddCustomCssClass()` before `Build()` | Consumers cannot inject custom classes via the `CssClass` parameter | Always call it as the last step before `Build()` | +| Omitting XML `<summary>` on `[Parameter]` members | No IntelliSense for library consumers | Document every `[Parameter]` with `<summary>` | +| Missing `aria-invalid` / `aria-describedby` on form inputs | Screen readers cannot announce validation errors | Wire from `IsInvalid` — see patterns.md Form Component section | +| Missing `[EditorRequired]` on required parameters | Silent null-reference failures at runtime | Mark required parameters with `[EditorRequired]` | +| Omitting `@attributes="AdditionalAttributes"` on the root element | Consumers cannot pass arbitrary HTML attributes | Forward `AdditionalAttributes` on the root element | +| Ignoring `ShouldRender` on pure display components | Unnecessary re-renders when used in large lists | Override `ShouldRender()` when render frequency should be controlled | +| Synchronous JS interop | Blocks the SignalR circuit in Server mode or WASM thread | Use `IJSRuntime` async APIs exclusively | +| Calling JS interop in `OnInitializedAsync` | No DOM element exists during server prerender | Call JS only from `OnAfterRenderAsync(firstRender: true)` | +| Mixing logic into `.razor` markup file | Hard to test and review; obscures the component's API surface | Put all `[Parameter]` declarations, state, and computed properties in `.razor.cs` | diff --git a/.github/skills/blazor/references/docs-template.md b/.github/skills/blazor/references/docs-template.md new file mode 100644 index 0000000..af28635 --- /dev/null +++ b/.github/skills/blazor/references/docs-template.md @@ -0,0 +1,95 @@ +# Documentation Page Template + +Every component must have a documentation page in `docs/`. The file location mirrors the component's namespace: + +- `BitBlazor.Components` → `docs/components/bit-xxx.md` +- `BitBlazor.Form` → `docs/form/bit-xxx.md` + +File names use kebab-case: `bit-text-field.md`, `bit-button-badge.md`. + +## Required Structure + +Use the following section order (see `docs/components/button.md` as a reference): + +```markdown +# BitXxx + +The `BitXxx` component represents a [brief description with Bootstrap Italia link](https://italia.github.io/bootstrap-italia/docs/...). + +## Namespace + +​```csharp +BitBlazor.Components (or BitBlazor.Form) +​``` + +## Description + +One or two sentences describing what the component does and when to use it. + +## Parameters + +| Name | Type | Required | Default | Description | +|------|------|----------|---------|-------------| +| `ChildContent` | `RenderFragment` | ✓ | - | The content rendered inside the component | +| `Color` | `Color` | ✓ | - | The color variant | +| `Size` | `Size` | ✗ | `Size.Default` | The size of the component | +| `CssClass` | `string?` | ✗ | `null` | Additional CSS classes to apply to the root element | +| `AdditionalAttributes` | `IDictionary<string, object>?` | ✗ | - | Additional HTML attributes forwarded to the root element | + +## Used Enumerations + +Document every enum type referenced in the Parameters table. + +### Color + +| Value | Description | +|-------|-------------| +| `Primary` | Primary color | +| `Secondary` | Secondary color | +| `Success` | Success / positive color | +| `Warning` | Warning color | +| `Danger` | Error / negative color | + +### Size + +| Value | Description | +|-------|-------------| +| `Default` | Standard size | +| `Small` | Reduced size | +| `Large` | Enlarged size | + +## Usage Examples + +### Basic usage + +​```razor +<BitXxx Color="Color.Primary"> + Content +</BitXxx> +​``` + +### [Variant name] + +​```razor +<BitXxx Color="Color.Secondary" Variant="Variant.Outline"> + Content +</BitXxx> +​``` + +### With additional attributes + +​```razor +<BitXxx Color="Color.Primary" data-testid="my-component"> + Content +</BitXxx> +​``` +``` + +## Conventions + +- **Link to Bootstrap Italia** in the opening sentence where the component maps to a Bootstrap Italia pattern +- **List all `[Parameter]` properties** including `CssClass` and `AdditionalAttributes` inherited from the base class +- **Mark `[EditorRequired]` parameters** with ✓ in the Required column +- **Enum tables** list every `Value` a consumer is expected to use (skip internal or future-only values) +- **Usage Examples**: at least one example per meaningful visual variant (color, size, disabled, with icon, etc.) +- **Do not** document internal computed properties or private fields — only `[Parameter]`-decorated members diff --git a/.github/skills/blazor/references/patterns.md b/.github/skills/blazor/references/patterns.md new file mode 100644 index 0000000..3a635ed --- /dev/null +++ b/.github/skills/blazor/references/patterns.md @@ -0,0 +1,165 @@ +# BitBlazor Component Patterns + +## General-Purpose Component + +A general-purpose component (alert, badge, card, button, modal, …) inherits `BitComponentBase`. Keep markup in the `.razor` file and all logic (parameters, computed properties) in the `.razor.cs` partial class. + +```razor +@* BitAlert.razor *@ +@namespace BitBlazor.Components +@inherits BitComponentBase + +<div class="@ComputeCssClasses()" role="alert" @attributes="AdditionalAttributes"> + @ChildContent +</div> +``` + +```csharp +// BitAlert.razor.cs +namespace BitBlazor.Components; + +/// <summary> +/// Represents an alert component using Bootstrap Italia styles. +/// </summary> +public partial class BitAlert : BitComponentBase +{ + /// <summary>Gets or sets the content of the alert.</summary> + [Parameter] + [EditorRequired] + public RenderFragment ChildContent { get; set; } = default!; + + /// <summary>Gets or sets the color variant of the alert.</summary> + [Parameter] + public Color Color { get; set; } = Color.Primary; + + private string ComputeCssClasses() + { + var builder = new CssClassBuilder("alert") + .Add($"alert-{Color.ToCssClass()}"); + AddCustomCssClass(builder); + return builder.Build(); + } +} +``` + +## Form Component + +Form components inherit `BitFormComponentBase<T>`. They must: + +- Override `FieldIdPrefix` to generate a unique `id` for the underlying input +- Override `SupportedTypes` to guard against unsupported `T` at construction time +- Wire `aria-invalid` and `aria-describedby` from `IsInvalid` (provided by the base class) + +```razor +@* BitCheckbox.razor *@ +@namespace BitBlazor.Form +@inherits BitFormComponentBase<bool> + +<div class="form-check"> + <input class="form-check-input" + type="checkbox" + id="@FieldId" + checked="@Value" + disabled="@Disabled" + aria-invalid="@(IsInvalid ? "true" : null)" + aria-describedby="@(IsInvalid ? $"{FieldId}-error" : null)" + @onchange="OnValueChangedAsync" + @attributes="AdditionalAttributes" /> + <label class="form-check-label" for="@FieldId"> + @Label + @if (Required) { <span aria-hidden="true"> *</span> } + </label> + @if (IsInvalid) + { + <span id="@FieldId-error" role="alert" class="invalid-feedback d-block"> + <ValidationMessage For="@For" /> + </span> + } +</div> +``` + +```csharp +// BitCheckbox.razor.cs +namespace BitBlazor.Form; + +/// <summary> +/// Represents a checkbox form component using Bootstrap Italia styles. +/// </summary> +public partial class BitCheckbox : BitFormComponentBase<bool> +{ + /// <inheritdoc/> + protected override string FieldIdPrefix => "checkbox"; + + /// <inheritdoc/> + protected override Type[] SupportedTypes => [typeof(bool)]; + + private async Task OnValueChangedAsync(ChangeEventArgs e) + { + var newValue = e.Value is bool b && b; + await ValueChanged.InvokeAsync(newValue); + } +} +``` + +## CSS Class Composition + +```csharp +// Good — CssClassBuilder with conditional classes +private string ComputeCssClasses() +{ + var builder = new CssClassBuilder("btn") + .Add($"btn-{Color.ToCssClass()}") + .Add("btn-lg", Size == Size.Large) + .Add("btn-sm", Size == Size.Small) + .Add("disabled", Disabled); + AddCustomCssClass(builder); // always before Build() + return builder.Build(); +} + +// Bad — manual concatenation +string css = "btn btn-" + color + (disabled ? " disabled" : ""); +``` + +`CssClassBuilder` deduplicates classes automatically. Use Bootstrap Italia CSS class names exclusively — do not invent custom class names. + +## Performance Best Practices + +**Use `@key` for list diffing:** + +```razor +@foreach (var item in items) +{ + <ItemComponent @key="item.Id" Item="@item" /> +} +``` + +**Override `ShouldRender` for pure display components:** + +```csharp +protected override bool ShouldRender() => _isDirty; +``` + +Note: Virtualization (`<Virtualize>`) is a consuming-app concern — document it in the component's usage examples rather than enforcing it in the library itself. + +## JS Interop + +Use `IJSRuntime` async APIs only. Inject via DI when a component genuinely requires JavaScript (e.g., focus management, clipboard, resize observers). + +```csharp +// BitXxx.razor.cs +[Inject] private IJSRuntime JS { get; set; } = default!; + +private ElementReference _elementRef; + +private async Task FocusAsync() + => await JS.InvokeVoidAsync("BitBlazor.focusElement", _elementRef); +``` + +```javascript +// wwwroot/js/bitblazor.js +window.BitBlazor = { + focusElement: (el) => el?.focus() +}; +``` + +Always call JS interop from `OnAfterRenderAsync` (never `OnInitializedAsync`) so the DOM element exists before the call is made. diff --git a/.github/skills/blazor/references/stories.md b/.github/skills/blazor/references/stories.md new file mode 100644 index 0000000..f753d6e --- /dev/null +++ b/.github/skills/blazor/references/stories.md @@ -0,0 +1,76 @@ +# Stories with BlazingStory + +Every component must have a corresponding story file so it appears in the live Storybook. Story files use the [BlazingStory](https://github.com/jsakamoto/BlazingStory) library. + +## File Location + +``` +stories/BitBlazor.Stories/Components/Stories/<Namespace>/BitXxx.stories.razor +``` + +The subfolder under `Stories/` must mirror the component's namespace segment — the same path used for the component's source and test files. For example, a component in `BitBlazor.Components` goes under `Stories/Components/`, and one in `BitBlazor.Form` goes under `Stories/Form/`. + +## Structure + +```razor +@attribute [Stories("Components/BitXxx")] + +<Stories TComponent="BitXxx"> + + @* Declare controls for each enum/value parameter *@ + <ArgType For="_ => _.Color" Control="ControlType.Select" /> + <ArgType For="_ => _.Size" Control="ControlType.Select" DefaultValue="Size.Default" /> + + @* Default story — shows the component in its most common state *@ + <Story Name="Default"> + <Arguments> + <Arg For="_ => _.Color" Value="Color.Primary" /> + </Arguments> + <Template> + <div class="py-2 px-2"> + <BitXxx @attributes="context.Args">Content</BitXxx> + </div> + </Template> + </Story> + + @* Variant stories — one per meaningful visual state *@ + <Story Name="Colors"> + <Template> + <div class="py-2 px-2"> + <BitXxx Color="Color.Primary">Primary</BitXxx> + <BitXxx Color="Color.Secondary">Secondary</BitXxx> + <BitXxx Color="Color.Success">Success</BitXxx> + <BitXxx Color="Color.Danger">Danger</BitXxx> + </div> + </Template> + </Story> + + <Story Name="Sizes"> + <Template> + <div class="py-2 px-2"> + <BitXxx Color="Color.Primary" Size="Size.Small">Small</BitXxx> + <BitXxx Color="Color.Primary" Size="Size.Default">Default</BitXxx> + <BitXxx Color="Color.Primary" Size="Size.Large">Large</BitXxx> + </div> + </Template> + </Story> + + <Story Name="Disabled"> + <Template> + <div class="py-2 px-2"> + <BitXxx Color="Color.Primary" Disabled="true">Disabled</BitXxx> + </div> + </Template> + </Story> + +</Stories> +``` + +## Conventions + +- The `[Stories(...)]` path must match the component's namespace segment (`"Components/BitXxx"` or `"Form/BitXxx"`) to appear in the correct Storybook section +- Always include a **Default** story that uses `@attributes="context.Args"` so the Storybook controls panel works +- Declare `<ArgType>` for every enum or selectable parameter; use `DefaultValue` to pre-select the most common value +- Add one story per meaningful visual variant: colors, sizes, disabled state, with icon, outline variant, etc. +- Wrap each story template in `<div class="py-2 px-2">` for consistent padding in the preview +- Form component stories must supply a minimal `EditForm` + model wrapper so the component is interactive in the preview diff --git a/.github/skills/blazor/references/testing.md b/.github/skills/blazor/references/testing.md new file mode 100644 index 0000000..2e654b9 --- /dev/null +++ b/.github/skills/blazor/references/testing.md @@ -0,0 +1,109 @@ +# Testing with bUnit + +Every component must have a corresponding test file. Tests live in `tests/BitBlazor.Test/` mirroring the `src/` structure. + +## File Location and Naming + +``` +src/BitBlazor/Components/Alert/BitAlert.razor +→ tests/BitBlazor.Test/Components/Alert/BitAlertTest.cs +``` + +Split tests by concern using multiple files per component when needed: + +- `BitXxxTest.Rendering.cs` — CSS class output, ARIA attribute, and markup tests +- `BitXxxTest.Behaviors.cs` — click, change, and event callback tests + +## Example + +```csharp +// tests/BitBlazor.Test/Components/Alert/BitAlertTest.cs +using BitBlazor.Components; +using Bunit; + +namespace BitBlazor.Test.Components.Alert; + +public class BitAlertTest +{ + [Fact] + public void BitAlert_Should_Render_With_Correct_Color_Class() + { + using var ctx = new BunitContext(); + + var component = ctx.Render<BitAlert>( + parameters => parameters + .Add(p => p.ChildContent, "This is an alert") + .Add(p => p.Color, Color.Danger)); + + Assert.True(component.Find("div").ClassList.Contains("alert-danger")); + } + + [Fact] + public void BitAlert_Should_Forward_Additional_Attributes() + { + using var ctx = new BunitContext(); + + var component = ctx.Render<BitAlert>( + parameters => parameters + .Add(p => p.ChildContent, "Alert") + .Add(p => p.Color, Color.Primary) + .AddUnmatched("data-testid", "my-alert")); + + Assert.Equal("my-alert", component.Find("div").GetAttribute("data-testid")); + } + + [Fact] + public void BitAlert_Should_Apply_Custom_CssClass() + { + using var ctx = new BunitContext(); + + var component = ctx.Render<BitAlert>( + parameters => parameters + .Add(p => p.ChildContent, "Alert") + .Add(p => p.Color, Color.Primary) + .Add(p => p.CssClass, "my-custom-class")); + + Assert.True(component.Find("div").ClassList.Contains("my-custom-class")); + } +} +``` + +## Form Component ARIA Test + +```csharp +[Fact] +public void BitCheckbox_Should_Set_AriaInvalid_When_Validation_Fails() +{ + using var ctx = new BunitContext(); + + // Arrange: put the field into an invalid state via EditContext + var model = new TestModel(); + var editContext = new EditContext(model); + + var component = ctx.Render<BitCheckbox>( + parameters => parameters + .Add(p => p.Label, "Accept terms") + .Add(p => p.For, () => model.Accepted) + .CascadeValue(editContext)); + + // Simulate validation failure + editContext.NotifyValidationStateChanged(); + + // Assert + var input = component.Find("input"); + Assert.Equal("true", input.GetAttribute("aria-invalid")); + Assert.NotNull(input.GetAttribute("aria-describedby")); +} +``` + +## Conventions + +- Use `BunitContext` (bUnit v2 API) — not the v1 `TestContext` +- Always test: + - CSS class output for each relevant parameter combination + - ARIA attributes (`aria-invalid`, `aria-label`, `role`, etc.) + - `EventCallback` invocation (click, change, etc.) + - `AdditionalAttributes` forwarding (`data-testid` pattern) + - `CssClass` injection (consumer custom class appears on root element) +- Name tests: `BitXxx_Should_<expected>_When_<condition>` +- One test class per component; split into multiple files by concern when a component has many tests From 8b1d443be66931fd3cafe05cdc3e3e81ed4861f1 Mon Sep 17 00:00:00 2001 From: albx <albi.mo87@hotmail.it> Date: Wed, 3 Jun 2026 07:49:11 +0200 Subject: [PATCH 3/9] #86 - moved skills inside .agents folder --- {.github => .agents}/skills/blazor/SKILL.md | 2 +- {.github => .agents}/skills/blazor/manifest.json | 0 {.github => .agents}/skills/blazor/references/anti-patterns.md | 0 {.github => .agents}/skills/blazor/references/docs-template.md | 0 {.github => .agents}/skills/blazor/references/patterns.md | 0 {.github => .agents}/skills/blazor/references/stories.md | 0 {.github => .agents}/skills/blazor/references/testing.md | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename {.github => .agents}/skills/blazor/SKILL.md (97%) rename {.github => .agents}/skills/blazor/manifest.json (100%) rename {.github => .agents}/skills/blazor/references/anti-patterns.md (100%) rename {.github => .agents}/skills/blazor/references/docs-template.md (100%) rename {.github => .agents}/skills/blazor/references/patterns.md (100%) rename {.github => .agents}/skills/blazor/references/stories.md (100%) rename {.github => .agents}/skills/blazor/references/testing.md (100%) diff --git a/.github/skills/blazor/SKILL.md b/.agents/skills/blazor/SKILL.md similarity index 97% rename from .github/skills/blazor/SKILL.md rename to .agents/skills/blazor/SKILL.md index 1286d49..7c59392 100644 --- a/.github/skills/blazor/SKILL.md +++ b/.agents/skills/blazor/SKILL.md @@ -98,7 +98,7 @@ Library components must work in **all render modes** used by the consuming appli ## Accessibility -Accessibility is a **first-class requirement** for every component in this library. The authoritative ruleset is [`a11y.instructions.md`](../../instructions/a11y.instructions.md), which is automatically active on every `.razor`, `.razor.cs`, and `.razor.css` edit — read it before authoring any interactive component. +Accessibility is a **first-class requirement** for every component in this library. The authoritative ruleset is [`a11y.instructions.md`](../../../.github/instructions/a11y.instructions.md), which is automatically active on every `.razor`, `.razor.cs`, and `.razor.css` edit — read it before authoring any interactive component. The checks most commonly missed when creating a new BitBlazor component are: diff --git a/.github/skills/blazor/manifest.json b/.agents/skills/blazor/manifest.json similarity index 100% rename from .github/skills/blazor/manifest.json rename to .agents/skills/blazor/manifest.json diff --git a/.github/skills/blazor/references/anti-patterns.md b/.agents/skills/blazor/references/anti-patterns.md similarity index 100% rename from .github/skills/blazor/references/anti-patterns.md rename to .agents/skills/blazor/references/anti-patterns.md diff --git a/.github/skills/blazor/references/docs-template.md b/.agents/skills/blazor/references/docs-template.md similarity index 100% rename from .github/skills/blazor/references/docs-template.md rename to .agents/skills/blazor/references/docs-template.md diff --git a/.github/skills/blazor/references/patterns.md b/.agents/skills/blazor/references/patterns.md similarity index 100% rename from .github/skills/blazor/references/patterns.md rename to .agents/skills/blazor/references/patterns.md diff --git a/.github/skills/blazor/references/stories.md b/.agents/skills/blazor/references/stories.md similarity index 100% rename from .github/skills/blazor/references/stories.md rename to .agents/skills/blazor/references/stories.md diff --git a/.github/skills/blazor/references/testing.md b/.agents/skills/blazor/references/testing.md similarity index 100% rename from .github/skills/blazor/references/testing.md rename to .agents/skills/blazor/references/testing.md From dce70274912505743088a9626f9dbc8e03e78be8 Mon Sep 17 00:00:00 2001 From: albx <albi.mo87@hotmail.it> Date: Fri, 5 Jun 2026 19:58:47 +0200 Subject: [PATCH 4/9] #86 - add github skills for write and read issues from repository --- .agents/skills/issue-reader/SKILL.md | 212 ++++++++++++++ .agents/skills/issue-reader/manifest.json | 5 + .agents/skills/issue-writer/SKILL.md | 322 ++++++++++++++++++++++ .agents/skills/issue-writer/manifest.json | 5 + 4 files changed, 544 insertions(+) create mode 100644 .agents/skills/issue-reader/SKILL.md create mode 100644 .agents/skills/issue-reader/manifest.json create mode 100644 .agents/skills/issue-writer/SKILL.md create mode 100644 .agents/skills/issue-writer/manifest.json diff --git a/.agents/skills/issue-reader/SKILL.md b/.agents/skills/issue-reader/SKILL.md new file mode 100644 index 0000000..432ce8c --- /dev/null +++ b/.agents/skills/issue-reader/SKILL.md @@ -0,0 +1,212 @@ +--- +name: issue-reader +description: "Retrieve GitHub issue information using the GitHub CLI (gh). USE FOR: viewing a specific issue by number, searching issues by topic or keyword, listing open/closed issues, and summarizing issue details. DO NOT USE FOR: creating issues (use issue-writer), commenting on issues, or closing issues." +compatibility: "Requires GitHub CLI (gh) authenticated to the target repository. Works on Windows (PowerShell), macOS, and Linux (bash/zsh)." +--- + +# Issue Reader — Retrieve GitHub Issue Information via GitHub CLI + +## Trigger On + +- user asks to look up, find, or show a GitHub issue +- user provides an issue number and wants its details +- user describes a topic and wants to search for matching issues +- user asks to list open or closed issues +- user wants a summary of what a specific issue is about + +## Prerequisites + +Verify `gh` is available and authenticated: + +```sh +gh auth status +``` + +If not authenticated, instruct the user to run: + +```sh +gh auth login +``` + +## Determine the Repository + +Always use the repository of the current workspace. Detect it automatically from the Git remote **before running any `gh` command** and store it for reuse: + +```sh +git remote get-url origin +``` + +If the command exits with a non-zero code or returns no output, stop and tell the user: "No Git remote named `origin` was found in this workspace. Please run this skill from inside the repository you want to query." + +Do **not** ask the user to provide or override the repository. All `gh` commands in this skill must use the detected `owner/repo` via `--repo owner/repo` so they always target the current workspace repository regardless of the shell's working directory. + +--- + +## Workflow + +### 1. Identify the retrieval mode + +Determine what the user wants based on their input: + +| User provides | Mode | +|---------------|------| +| An issue number (e.g., `#42` or `42`) | **View by number** | +| A keyword, topic, or description | **Search by topic** | +| No specific issue — wants an overview | **List issues** | + +--- + +### 2a. View a specific issue by number + +Fetch the full details of a single issue: + +```sh +gh issue view 42 --repo owner/repo +``` + +To get machine-readable JSON (useful for further processing): + +```sh +gh issue view 42 --repo owner/repo --json number,title,body,state,labels,assignees,milestone,createdAt,updatedAt,url +``` + +Show the issue in the browser: + +```sh +gh issue view 42 --repo owner/repo --web +``` + +**Output summary to present to the user:** + +| Field | Description | +|-------|-------------| +| `number` | Issue number | +| `title` | Issue title | +| `state` | `OPEN` or `CLOSED` | +| `labels` | Assigned labels | +| `assignees` | Assigned users | +| `milestone` | Associated milestone | +| `body` | Full issue description | +| `url` | Direct link | + +--- + +### 2b. Search for issues by topic or keyword + +Use `--search` to filter issues by keyword, label, assignee, or state. The search query supports [GitHub search syntax](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests). All commands target the current workspace repository. + +**Search by keyword:** + +```sh +gh issue list --repo owner/repo --search "<keyword>" +``` + +**Search open issues only (default):** + +```sh +gh issue list --repo owner/repo --state open --search "<keyword>" +``` + +**Search closed issues:** + +```sh +gh issue list --repo owner/repo --state closed --search "<keyword>" +``` + +**Search by label:** + +```sh +gh issue list --repo owner/repo --label "bug" --search "<keyword>" +``` + +**Limit results (default is 30):** + +```sh +gh issue list --repo owner/repo --search "<keyword>" --limit 10 +``` + +**Output as JSON for structured processing:** + +```sh +gh issue list --repo owner/repo --search "<keyword>" \ + --json number,title,state,labels,assignees,url +``` + +--- + +### 2c. List issues (no specific search term) + +All commands target the current workspace repository. + +**All open issues:** + +```sh +gh issue list --repo owner/repo +``` + +**All issues (open and closed):** + +```sh +gh issue list --repo owner/repo --state all +``` + +**Issues assigned to a specific user:** + +```sh +gh issue list --repo owner/repo --assignee "username" +``` + +**Issues with a specific label:** + +```sh +gh issue list --repo owner/repo --label "enhancement" +``` + +**Issues for a specific milestone:** + +```sh +gh issue list --repo owner/repo --milestone "v1.0" +``` + +--- + +### 3. Present results to the user + +**For a single issue (view mode):** Present the key fields in a readable summary. Include the direct URL at the end. + +**For a list or search result:** Present as a markdown table with columns: `#`, `Title`, `State`, `Labels`, `URL`. Limit to the most relevant results. If the list is long, ask the user if they want to narrow the search. + +--- + +## Useful Field Reference for JSON Output + +When using `--json`, available fields include: + +| Field | Type | Description | +|-------|------|-------------| +| `number` | int | Issue number | +| `title` | string | Issue title | +| `body` | string | Issue description (markdown) | +| `state` | string | `OPEN` or `CLOSED` | +| `labels` | array | Label names | +| `assignees` | array | GitHub usernames | +| `milestone` | object | Milestone title and due date | +| `author` | object | Issue author login | +| `comments` | array | Issue comments | +| `createdAt` | string | ISO 8601 creation date | +| `updatedAt` | string | ISO 8601 last update date | +| `closedAt` | string | ISO 8601 close date (if closed) | +| `url` | string | HTML URL of the issue | + +--- + +## Error Handling + +| Error | Fix | +|-------|-----| +| `gh: command not found` | Install GitHub CLI: https://cli.github.com | +| `You are not logged into any GitHub hosts` | Run `gh auth login` | +| `Could not resolve to an issue` | Verify the issue number exists; run `gh issue list` to confirm | +| `Repository not found` | Ask the user to verify the `owner/repo` slug and re-run with `--repo "<corrected-owner/repo>"` | +| `Permission denied` | Ensure the authenticated user has read access to the repository | +| No results from `--search` | Broaden the query, remove filters, or try `--state all` | diff --git a/.agents/skills/issue-reader/manifest.json b/.agents/skills/issue-reader/manifest.json new file mode 100644 index 0000000..6905eb6 --- /dev/null +++ b/.agents/skills/issue-reader/manifest.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.0", + "category": "DevOps", + "package_prefix": "GitHub" +} diff --git a/.agents/skills/issue-writer/SKILL.md b/.agents/skills/issue-writer/SKILL.md new file mode 100644 index 0000000..871c77c --- /dev/null +++ b/.agents/skills/issue-writer/SKILL.md @@ -0,0 +1,322 @@ +--- +name: issue-writer +description: "Create GitHub issues using the GitHub CLI (gh). USE FOR: opening a new issue, filing a bug report, requesting a feature, creating a task with labels, assignees, and milestones. DO NOT USE FOR: listing issues (use gh issue list), commenting on PRs, or pushing code changes." +compatibility: "Requires GitHub CLI (gh) authenticated to the target repository. Works on Windows (PowerShell), macOS, and Linux (bash/zsh)." +--- + +# Issue Writer — Create GitHub Issues via GitHub CLI + +## Trigger On + +- user wants to create, open, or file a GitHub issue +- user asks to report a bug, request a feature, or log a task +- user provides a title, description, or label for an issue to be opened +- user asks to create an issue in the current repository + +## Prerequisites + +Before creating issues, verify `gh` is available and authenticated: + +```sh +gh auth status +``` + +If not authenticated, instruct the user to run: + +```sh +gh auth login +``` + +## Issue Templates + +This repository defines two issue templates in `.github/ISSUE_TEMPLATE/`. Select one when the user's intent matches a template; use a **blank issue** only when it fits neither. + +### `bug_report.md` — Bug Report + +Use when the user reports something that is not working as expected. + +- **Title prefix**: `[BUG]` +- **Suggested label**: `bug` — verify it exists first with `gh label list` +- **Required sections**: + - `### Describe the bug` — concise description of what is wrong + - `### To Reproduce` — numbered steps to reproduce + - `### Expected behavior` — what should have happened + - `### Screenshots` *(optional)* + - `### Desktop` — OS, Browser, Version + - `### Smartphone` *(optional)* — Device, OS, Browser, Version + - `### Additional context` *(optional)* + +### `new-feature.md` — New Feature + +Use when the user requests a new feature or a new component. + +- **Title prefix**: `[FEATURE]` +- **Suggested label**: `enhancement` — verify it exists first with `gh label list` +- **Required sections**: + - Brief description of the feature + - `### Requirements` — bullet list of what needs to be implemented + - `### Acceptance Criteria` — definition of done (tests, docs, …) + - `### References` — links to external resources + +--- + +## Workflow + +### 1. Identify the template + +Ask the user (or infer from context) which type of issue this is: + +| User intent | Template to use | +|-------------|----------------| +| Something is broken / not working | `bug_report.md` | +| New feature / new component / improvement | `new-feature.md` | +| Task, question, chore, or anything else | Blank issue (no template) | + +### 2. Gather information + +Collect the fields required by the chosen template (ask only for missing ones): + +**Bug report fields:** + +| Field | Required | Notes | +|-------|----------|-------| +| Title | Yes | Prefix with `[BUG]` | +| Describe the bug | Yes | | +| Steps to reproduce | Yes | Numbered list | +| Expected behavior | Yes | | +| OS / Browser / Version | Yes | | +| Screenshots | No | | +| Additional context | No | | +| Labels | No | Suggest `bug` | +| Assignees | No | | +| Milestone | No | | + +**New feature fields:** + +| Field | Required | Notes | +|-------|----------|-------| +| Title | Yes | Prefix with `[FEATURE]` | +| Brief description | Yes | | +| Requirements | Yes | Bullet list | +| Acceptance criteria | Yes | Include tests, docs | +| References | No | External links | +| Labels | No | Suggest `enhancement` | +| Assignees | No | | +| Milestone | No | | + +**Blank issue fields:** + +| Field | Required | Notes | +|-------|----------|-------| +| Title | Yes | Clear, concise summary | +| Body | No | Free-form markdown; omit if not provided | +| Labels | No | | +| Assignees | No | | +| Milestone | No | | + +### 3. Determine the repository + +Always use the repository of the current workspace. Detect it automatically from the Git remote **before running any `gh` command** and store it for reuse: + +```sh +git remote get-url origin +``` + +If the command exits with a non-zero code or returns no output, stop and tell the user: "No Git remote named `origin` was found in this workspace. Please run this skill from inside the repository where you want to create the issue." + +Do **not** ask the user to provide or override the repository. All `gh` commands must use the detected `owner/repo` via `--repo owner/repo`. + +### 4. Compose the body + +Build the body string following the exact section headings from the template. Write it to a temporary file before calling `gh issue create` to avoid quoting issues. Use the command that matches your shell: + +**Windows (PowerShell):** + +```powershell +$body = @' +<body content — see template below> +'@ +Set-Content -Path "$env:TEMP\issue-body.md" -Value $body -Encoding UTF8 +``` + +**macOS / Linux (bash/zsh):** + +```sh +cat > /tmp/issue-body.md << 'EOF' +<body content — see template below> +EOF +``` + +Then pass the temp file to `gh issue create`: +- Windows: `--body-file "$env:TEMP\issue-body.md"` +- macOS/Linux: `--body-file /tmp/issue-body.md` + +**Bug report body:** + +```markdown +### Describe the bug +<description> + +### To Reproduce +Steps to reproduce the behavior: +1. <step 1> +2. <step 2> +3. <step 3> +4. See error + +### Expected behavior +<expected behavior> + +### Screenshots +<screenshots or "N/A"> + +### Desktop (please complete the following information): + - OS: <os> + - Browser: <browser> + - Version: <version> + +### Additional context +<additional context or "N/A"> +``` + +**New feature body:** + +```markdown +<brief description> + +### Requirements: +- <requirement 1> +- <requirement 2> + +### Acceptance Criteria: +- Add tests +- Add documentation +- <other criteria> + +### References +- <link or "N/A"> +``` + +### 5. Create the issue + +Use `--body-file` to pass the composed body. Only add `--label` if the label already exists in the repository (verify with `gh label list`). `--body-file` and `--template` are mutually exclusive in the GitHub CLI — do not pass both. + +**Bug report:** + +```powershell +# Windows (PowerShell) +gh issue create ` + --title "[BUG] <short description>" ` + --body-file "$env:TEMP\issue-body.md" ` + --label "bug" ` # only if the label exists + --repo "owner/repo" +``` + +```sh +# macOS / Linux +gh issue create \ + --title "[BUG] <short description>" \ + --body-file /tmp/issue-body.md \ + --label "bug" \ # only if the label exists + --repo "owner/repo" +``` + +**New feature:** + +```powershell +# Windows (PowerShell) +gh issue create ` + --title "[FEATURE] <short description>" ` + --body-file "$env:TEMP\issue-body.md" ` + --label "enhancement" ` # only if the label exists + --repo "owner/repo" +``` + +```sh +# macOS / Linux +gh issue create \ + --title "[FEATURE] <short description>" \ + --body-file /tmp/issue-body.md \ + --label "enhancement" \ # only if the label exists + --repo "owner/repo" +``` + +For blank issues: if the user provides a body, pass it with `--body "<text>"`. If the user provides no body, omit all body flags entirely. Do not use `--body-file` for blank issues. + +```sh +# Title only +gh issue create \ + --title "<short description>" \ + --repo "owner/repo" + +# Title + body +gh issue create \ + --title "<short description>" \ + --body "<free-form description>" \ + --repo "owner/repo" +``` + +Add `--label` once per label when the user requests multiple labels (e.g., `--label "bug" --label "good first issue"`). Add `--assignee` once per assignee and `--milestone "<name>"` when provided. + +### 6. Confirm and report + +After the command succeeds, `gh` prints the URL of the created issue. Share it with the user: + +``` +https://github.com/owner/repo/issues/123 +``` + +## Available Labels (BitBlazor defaults) + +Check existing labels before assigning: + +```sh +gh label list --repo owner/repo +``` + +Common labels used in this repository: + +| Label | Use When | +|-------|----------| +| `bug` | Something is not working as expected | +| `enhancement` | New feature or improvement request | +| `documentation` | Documentation is missing or incorrect | +| `good first issue` | Suitable for new contributors | +| `help wanted` | Extra attention or community help needed | +| `question` | Further clarification is needed | +| `components` | issues related to the components | +| `CI\CD` | issues related to the CI\CD pipelines | + +## Useful Supplementary Commands + +```sh +# List open issues +gh issue list + +# View a specific issue +gh issue view 42 + +# Close an issue +gh issue close 42 --comment "Resolved in PR #99" + +# Reopen an issue +gh issue reopen 42 + +# List available milestones +gh api repos/{owner}/{repo}/milestones --jq '.[].title' + +# List available assignable users +gh api repos/{owner}/{repo}/collaborators --jq '.[].login' +``` + +## Error Handling + +| Error | Fix | +|-------|-----| +| `gh: command not found` | Install GitHub CLI: https://cli.github.com | +| `You are not logged into any GitHub hosts` | Run `gh auth login` | +| `Label not found` | Run `gh label list` to see valid labels; create missing ones with `gh label create` | +| `No such milestone` | Run `gh api repos/{owner}/{repo}/milestones` to list milestones | +| `Assignee is not a collaborator` | Run `gh api repos/{owner}/{repo}/collaborators --jq '.[].login'` to list valid assignees, then re-run with a valid `--assignee` value | +| `Repository not found` | Ask the user to verify the `owner/repo` slug and re-run with the corrected value using `--repo "<corrected-owner/repo>"` | +| `Permission denied` | Ensure the authenticated user has write access to the repository | diff --git a/.agents/skills/issue-writer/manifest.json b/.agents/skills/issue-writer/manifest.json new file mode 100644 index 0000000..6905eb6 --- /dev/null +++ b/.agents/skills/issue-writer/manifest.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.0", + "category": "DevOps", + "package_prefix": "GitHub" +} From 3a1a5eb0cfc1099f8e622155c26da116ba65f31f Mon Sep 17 00:00:00 2001 From: Alberto Mori <albi.mo87@hotmail.it> Date: Fri, 5 Jun 2026 22:25:48 +0200 Subject: [PATCH 5/9] #86 - add dev, reviewer and pm agents --- .github/agents/dev.agent.md | 85 ++++++++++++++++++ .github/agents/pm.agent.md | 93 +++++++++++++++++++ .github/agents/reviewer.agent.md | 148 +++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+) create mode 100644 .github/agents/dev.agent.md create mode 100644 .github/agents/pm.agent.md create mode 100644 .github/agents/reviewer.agent.md diff --git a/.github/agents/dev.agent.md b/.github/agents/dev.agent.md new file mode 100644 index 0000000..7246351 --- /dev/null +++ b/.github/agents/dev.agent.md @@ -0,0 +1,85 @@ +--- +name: dev +description: "BitBlazor feature developer. USE FOR: implementing features or tasks from GitHub issues, adding or extending Blazor UI kit components, writing bUnit tests, creating story files, updating documentation. Fetches issue details automatically when given an issue number. Stops to ask clarification before coding if requirements are ambiguous. DO NOT USE FOR: non-Blazor tasks, infrastructure changes, Azure deployments, or general Q&A." +model: "Claude Sonnet 4.6 (copilot)" +tools: [read, edit, search, execute, agent] +argument-hint: "Issue number or task description. Examples: '#86 implement BitTooltip', 'add Disabled parameter to BitBadge', 'write tests for BitAlert'" +--- + +You are **dev**, the BitBlazor feature developer agent. Your job is to implement features and tasks for the BitBlazor UI kit library — an accessible, Bootstrap Italia-styled Blazor component library for .NET 9+. + +## Skills + +You have two skills at your disposal. Load and follow their full instructions before acting: + +- **blazor** skill at `.agents/skills/blazor/SKILL.md` — for authoring, extending, and testing BitBlazor components. +- **issue-reader** skill at `.agents/skills/issue-reader/SKILL.md` — for fetching GitHub issue details via the `gh` CLI. + +Always read both skill files at the start of every session. + +## Workflow + +### Step 1 — Understand the task + +- If the user provides an **issue number**, invoke the **issue-reader** skill to fetch the issue title, body, labels, and linked comments before doing anything else. +- If **no issue number** is provided: + 1. Use the **issue-reader** skill to **search existing issues** using keywords from the user's description. + 2. If one or more matching issues are found, present them to the user and ask which one (if any) to use as the source of requirements. + 3. If no matching issue is found, extract requirements from the user's description, summarize them clearly, and **ask the user to confirm** the requirements before proceeding. + +### Step 2 — Clarify before coding + +Before writing a single line of code, confirm you have answers to **all** of the following: + +| Question | Why it matters | +|----------|----------------| +| Which component(s) are affected? | Determines files to create or edit | +| What new parameters or behaviors are required? | Drives the implementation surface | +| Are there acceptance criteria or examples? | Guards against scope creep | +| Is this a full end-to-end feature or a single sub-task? | Determines output scope | + +**If any answer is missing or ambiguous, STOP and ask the user for clarification. Do not proceed with implementation until all questions are resolved.** + +### Step 3 — Plan + +Before editing files, outline: +1. Files to create (component, test, story, docs) +2. Files to modify (existing component or base class) +3. Acceptance criteria from the issue or task description + +Present the plan to the user. If the plan looks non-trivial, ask for explicit confirmation before proceeding. + +### Step 4 — Implement + +Follow the **blazor** skill's Component Authoring Workflow exactly: + +1. Choose the right base class (`BitComponentBase`, `BitFormComponentBase<T>`, or `BitInputFieldBase<T>`) +2. Create or update the `.razor` and `.razor.cs` files +3. Compose CSS with `CssClassBuilder`; call `AddCustomCssClass()` before `Build()` +4. Use semantic HTML and wire ARIA attributes for accessibility (WCAG 2.2 AA) +5. Write bUnit tests in `tests/BitBlazor.Test/` +6. Create or update a BlazingStory file in `stories/BitBlazor.Stories/` +7. Write or update the documentation page in `docs/` + +For a **single sub-task**, implement only the scoped deliverable; do not touch unrelated files. + +### Step 5 — Verify + +After implementation, run the build and test suite to confirm no regressions: + +```sh +dotnet build BitBlazor.sln +dotnet test tests/BitBlazor.Test/BitBlazor.Test.csproj +``` + +Report the outcome. If tests fail, fix them before finishing. + +## Constraints + +- **NEVER** start coding if requirements are unclear — always ask first. +- **NEVER** inherit directly from `ComponentBase`; use the BitBlazor base class hierarchy. +- **NEVER** use inline styles or hardcoded color values; use Bootstrap Italia CSS classes. +- **NEVER** add features beyond what the issue or task explicitly requires. +- **NEVER** remove or bypass accessibility attributes (`aria-*`, `role`, `tabindex`). +- **NEVER** use positive `tabindex` values (only `0` or `-1`). +- **ONLY** target files inside `src/BitBlazor/`, `tests/BitBlazor.Test/`, `stories/BitBlazor.Stories/`, and `docs/`. diff --git a/.github/agents/pm.agent.md b/.github/agents/pm.agent.md new file mode 100644 index 0000000..6cbd518 --- /dev/null +++ b/.github/agents/pm.agent.md @@ -0,0 +1,93 @@ +--- +name: pm +description: "BitBlazor product manager. USE FOR: creating GitHub issues (bug reports, feature requests, tasks); collecting and refining requirements from user descriptions; checking for duplicate issues before filing; filing well-structured issues with correct labels. DO NOT USE FOR: implementing code, reviewing PRs, running tests, or any non-issue-management task." +model: "Claude Haiku 4.5 (copilot)" +tools: [execute, agent] +argument-hint: "Describe what you want to report or request. Examples: 'BitAlert does not render on dark mode', 'add a Tooltip component', 'text field ignores Disabled parameter'" +--- + +You are **pm**, the BitBlazor product manager agent. Your sole job is to create well-structured, duplicate-free GitHub issues for the BitBlazor UI kit library. + +## Skills + +Load and follow the full instructions of these two skills at the start of every session before doing anything else: + +- **issue-reader** skill at `.agents/skills/issue-reader/SKILL.md` — for searching existing issues and detecting duplicates. +- **issue-writer** skill at `.agents/skills/issue-writer/SKILL.md` — for composing and filing issues. + +## Workflow + +### Step 1 — Understand the request + +Read the user's input carefully. Classify it as one of: + +| Type | Indicator | +|------|-----------| +| Bug report | Something is broken, not working, or behaving unexpectedly | +| Feature request | New component, new parameter, improvement, or enhancement | +| Task / chore | Documentation, CI/CD, refactoring, or maintenance | +| Unclear | Intent is ambiguous or too vague to classify | + +If the type is **unclear**, ask the user one focused question to resolve it before continuing. + +### Step 2 — Gather requirements (STOP if ambiguous) + +Collect the fields required by the chosen template from the **issue-writer** skill. Ask only for fields that are missing or ambiguous. Do not ask for optional fields unless the user volunteers them. + +**Mandatory clarifying questions** (ask in a single grouped message if multiple are missing): + +| Template | Required fields to confirm | +|----------|---------------------------| +| Bug report | What is wrong? Steps to reproduce? Expected behavior? OS/browser/version? | +| Feature request | What should the feature do? Requirements (bullet list)? Acceptance criteria? | +| Task | What needs to be done? Definition of done? | + +**Do not proceed to Step 3 until every required field is answered.** + +### Step 3 — Duplicate check (mandatory) + +Before creating any issue, search existing issues using keywords from the title and description: + +```sh +gh issue list --repo owner/repo --state all --search "<keywords>" --json number,title,state,url +``` + +- Run at least one search with 2–3 representative keywords. +- If one or more similar issues are found, present them to the user: + > "I found the following existing issues that may be related. Please confirm this is not a duplicate before I proceed." +- If the user confirms it is a duplicate → stop and link to the existing issue. +- If the user confirms it is new (or no matches found) → continue to Step 4. + +### Step 4 — Summarize and confirm + +Present a concise summary of the issue to be created: + +``` +Type: [Bug / Feature / Task] +Title: <proposed title with prefix> +Labels: <labels> +--- +<formatted body preview> +``` + +Ask: "Does this look correct? Should I create this issue?" + +Only proceed after the user confirms. + +### Step 5 — Create the issue + +Follow the **issue-writer** skill exactly: +1. Detect the repository from `git remote get-url origin`. +2. Verify that any proposed labels exist with `gh label list`. +3. Write the body to a temp file. +4. Run `gh issue create` with `--body-file`, `--title`, `--label` (if verified), and `--repo`. +5. Share the resulting issue URL with the user. + +## Constraints + +- **NEVER create an issue without the user's explicit confirmation** (Step 4). +- **NEVER skip the duplicate check** (Step 3). +- **NEVER implement code** — your role ends when the issue is filed. +- **NEVER ask for a repository URL** — always detect it from the Git remote. +- Ask clarifying questions in a single grouped message, not one at a time. +- Use `vscode_askQuestions` when available to collect missing fields interactively. diff --git a/.github/agents/reviewer.agent.md b/.github/agents/reviewer.agent.md new file mode 100644 index 0000000..7223fce --- /dev/null +++ b/.github/agents/reviewer.agent.md @@ -0,0 +1,148 @@ +--- +name: reviewer +description: "BitBlazor code and component reviewer. USE FOR: reviewing a component or feature against BitBlazor coding standards and WCAG 2.2 AA accessibility requirements; checking a pull request or diff; auditing ARIA usage, keyboard interactions, form patterns, and CSS class composition; verifying bUnit test coverage and story completeness. DO NOT USE FOR: implementing features (use dev agent), Azure/infrastructure work, or general coding Q&A outside the BitBlazor library." +model: "Claude Sonnet 4.5 (copilot)" +tools: [read, search, agent, execute] +argument-hint: "Component name, file path, or feature description. Examples: 'review BitModal', 'check a11y on BitTextField', 'review PR changes in src/BitBlazor/Form/Toggle/'" +--- + +You are **reviewer**, the BitBlazor code and accessibility reviewer. Your job is to analyze components, features, and diffs from the BitBlazor UI kit library and produce a structured, actionable review covering **coding standards** and **WCAG 2.2 AA accessibility compliance**. + +You are **read-only**. You never edit files. You produce a review report and stop. + +## Skills and References to Load + +At the start of every review session, load these in full before analyzing anything: + +### Coding Standards +- **blazor** skill at `.agents/skills/blazor/SKILL.md` — load this first; it defines the component authoring workflow, base class hierarchy, and pointers to the four reference files (`patterns.md`, `anti-patterns.md`, `testing.md`, `stories.md`). After loading `SKILL.md`, read all four reference files it links to. + +### Accessibility Rules +- `.github/instructions/a11y.instructions.md` — WCAG 2.2 AA rules, severity levels, and Blazor-specific anti-patterns (BL1–BL6) + +### Issue Context (when needed) +- **issue-reader** skill at `.agents/skills/issue-reader/SKILL.md` — load when requirements are unclear or an issue number is provided; use it to fetch the issue title, body, acceptance criteria, and labels before determining the review scope. + +Load the blazor skill and all its referenced files, then the a11y instructions, before proceeding to analysis. + +## Constraints + +- DO NOT edit or create any file. +- DO NOT suggest changes outside the scope of what was asked. +- DO NOT invent rules not found in the loaded references. +- ONLY report findings that can be traced back to a loaded reference or WCAG criterion. + +## Workflow + +### Step 1 — Identify scope + +Determine what to review using the table below: + +| Input | Action | +|-------|--------| +| Component name (e.g., `BitModal`) | Locate `.razor`, `.razor.cs`, `.razor.css` under `src/BitBlazor/`, test file under `tests/BitBlazor.Test/`, story under `stories/BitBlazor.Stories/` | +| File path or glob | Read those files directly | +| Issue number (e.g., `#42`) | Load the **issue-reader** skill, fetch the issue, extract requirements and acceptance criteria, then locate the relevant files | +| Vague feature description | Load the **issue-reader** skill, search for matching issues by keyword; if a matching issue is found, use it as the requirements baseline before locating files | +| No clear scope | Ask the user for a component name, file path, or issue number before proceeding | + +Use the `Explore` subagent for thorough codebase discovery when the scope is unclear after the above steps. + +### Step 2 — Load references + +Read all five reference files listed above. Do not skip any. + +### Step 3 — Analyze + +Review the identified files against **two dimensions**: + +#### A. Coding Standards +Check against `patterns.md` and `anti-patterns.md`: + +| Check | Reference | +|-------|-----------| +| Correct base class (`BitComponentBase`, `BitFormComponentBase<T>`, `BitInputFieldBase<T>`) | patterns.md | +| `CssClassBuilder` used; `AddCustomCssClass()` called before `Build()` | patterns.md | +| Enum types used for Color, Size, Variant parameters | patterns.md | +| All `[Parameter]` members have XML `<summary>` | patterns.md | +| `[EditorRequired]` on required parameters | anti-patterns.md | +| `@attributes="AdditionalAttributes"` forwarded on root element | anti-patterns.md | +| Logic separated into `.razor.cs`; no inline logic in `.razor` | anti-patterns.md | +| JS interop only in `OnAfterRenderAsync(firstRender: true)` and async | anti-patterns.md | +| `ShouldRender` overridden on pure display components if appropriate | anti-patterns.md | + +#### B. Accessibility (WCAG 2.2 AA) +Check against `a11y.instructions.md` with focus on Blazor-specific patterns (BL1–BL6): + +| Check | WCAG / Rule | +|-------|-------------| +| Interactive elements use native HTML (`<button>`, `<input>`, `<a>`) | S8, BL1 | +| Icon-only buttons have `aria-label` | A6 | +| Form inputs have associated `<label>` or `aria-label` | F1 | +| Validation errors linked via `aria-invalid` + `aria-describedby` | F2, BL5 | +| Modals trap focus and restore it on close | K3, BL4 | +| Conditional rendering restores or moves focus | BL6 | +| `@onkeydown` handler on any custom interactive element | K1, BL1 | +| No positive `tabindex` values | K2 | +| No `outline: none` without `:focus-visible` replacement | K5 | +| Live regions on dynamic/status content | A8 | +| Color not the sole conveyor of meaning | V2 | +| Text and non-text contrast ratios | V1, V3 | +| Routable pages include `<PageTitle>` | BL2 | +| No `aria-hidden="true"` on focusable elements | A2 | + +#### C. Test Coverage +Check against `testing.md`: +- Tests exist for all public parameters +- Tests cover invalid/error states for form components +- Keyboard interaction is tested for interactive components + +#### D. Story Completeness +Check against `stories.md`: +- A story file exists and mirrors the component namespace +- Key parameter combinations have dedicated stories + +### Step 4 — Produce the review report + +Output a single structured report in this format: + +--- + +## Review: `<ComponentOrFeatureName>` + +### Summary +One-paragraph overview of the overall quality and the most important findings. + +### Findings + +#### 🔴 CRITICAL +> Issues that must be fixed before merge — users cannot access content or the component violates a hard accessibility requirement. + +| # | File | Line(s) | Issue | Rule | +|---|------|---------|-------|------| +| C1 | `path/to/file` | 42 | Description | WCAG 4.1.2 / A6 | + +#### 🟠 IMPORTANT +> Significant barriers or standard violations that should be fixed in the same sprint. + +| # | File | Line(s) | Issue | Rule | +|---|------|---------|-------|------| +| I1 | `path/to/file` | 12–18 | Description | anti-patterns.md | + +#### 🟡 SUGGESTION +> Improvements that enhance quality or accessibility without blocking release. + +| # | File | Line(s) | Issue | Rule | +|---|------|---------|-------|------| +| S1 | `path/to/file` | — | Description | patterns.md | + +### What Looks Good +Bullet list of things that are correctly implemented and worth noting. + +### Recommended Next Steps +Ordered list of the top 3–5 actions to take, referencing finding IDs. + +--- + +If there are **no findings** in a severity category, write "None." Do not omit the heading. +If the scope is incomplete (e.g., missing test file or story file), note it under the appropriate severity. From 49481c15422d25867f212a25a7cea317595bc254 Mon Sep 17 00:00:00 2001 From: albx <albi.mo87@hotmail.it> Date: Mon, 8 Jun 2026 07:28:05 +0200 Subject: [PATCH 6/9] #86 - rename agents --- .../agents/{reviewer.agent.md => code-reviewer.agent.md} | 6 +++--- .github/agents/{dev.agent.md => developer.agent.md} | 4 ++-- .github/agents/{pm.agent.md => projectmanager.agent.md} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename .github/agents/{reviewer.agent.md => code-reviewer.agent.md} (94%) rename .github/agents/{dev.agent.md => developer.agent.md} (94%) rename .github/agents/{pm.agent.md => projectmanager.agent.md} (95%) diff --git a/.github/agents/reviewer.agent.md b/.github/agents/code-reviewer.agent.md similarity index 94% rename from .github/agents/reviewer.agent.md rename to .github/agents/code-reviewer.agent.md index 7223fce..891c2b6 100644 --- a/.github/agents/reviewer.agent.md +++ b/.github/agents/code-reviewer.agent.md @@ -1,12 +1,12 @@ --- -name: reviewer -description: "BitBlazor code and component reviewer. USE FOR: reviewing a component or feature against BitBlazor coding standards and WCAG 2.2 AA accessibility requirements; checking a pull request or diff; auditing ARIA usage, keyboard interactions, form patterns, and CSS class composition; verifying bUnit test coverage and story completeness. DO NOT USE FOR: implementing features (use dev agent), Azure/infrastructure work, or general coding Q&A outside the BitBlazor library." +name: code-reviewer +description: "BitBlazor code and component reviewer. USE FOR: reviewing a component or feature against BitBlazor coding standards and WCAG 2.2 AA accessibility requirements; checking a pull request or diff; auditing ARIA usage, keyboard interactions, form patterns, and CSS class composition; verifying bUnit test coverage and story completeness. DO NOT USE FOR: implementing features (use developer agent), Azure/infrastructure work, or general coding Q&A outside the BitBlazor library." model: "Claude Sonnet 4.5 (copilot)" tools: [read, search, agent, execute] argument-hint: "Component name, file path, or feature description. Examples: 'review BitModal', 'check a11y on BitTextField', 'review PR changes in src/BitBlazor/Form/Toggle/'" --- -You are **reviewer**, the BitBlazor code and accessibility reviewer. Your job is to analyze components, features, and diffs from the BitBlazor UI kit library and produce a structured, actionable review covering **coding standards** and **WCAG 2.2 AA accessibility compliance**. +You are **code-reviewer**, the BitBlazor code and accessibility reviewer. Your job is to analyze components, features, and diffs from the BitBlazor UI kit library and produce a structured, actionable review covering **coding standards** and **WCAG 2.2 AA accessibility compliance**. You are **read-only**. You never edit files. You produce a review report and stop. diff --git a/.github/agents/dev.agent.md b/.github/agents/developer.agent.md similarity index 94% rename from .github/agents/dev.agent.md rename to .github/agents/developer.agent.md index 7246351..a89db04 100644 --- a/.github/agents/dev.agent.md +++ b/.github/agents/developer.agent.md @@ -1,12 +1,12 @@ --- -name: dev +name: developer description: "BitBlazor feature developer. USE FOR: implementing features or tasks from GitHub issues, adding or extending Blazor UI kit components, writing bUnit tests, creating story files, updating documentation. Fetches issue details automatically when given an issue number. Stops to ask clarification before coding if requirements are ambiguous. DO NOT USE FOR: non-Blazor tasks, infrastructure changes, Azure deployments, or general Q&A." model: "Claude Sonnet 4.6 (copilot)" tools: [read, edit, search, execute, agent] argument-hint: "Issue number or task description. Examples: '#86 implement BitTooltip', 'add Disabled parameter to BitBadge', 'write tests for BitAlert'" --- -You are **dev**, the BitBlazor feature developer agent. Your job is to implement features and tasks for the BitBlazor UI kit library — an accessible, Bootstrap Italia-styled Blazor component library for .NET 9+. +You are **developer**, the BitBlazor feature developer agent. Your job is to implement features and tasks for the BitBlazor UI kit library — an accessible, Bootstrap Italia-styled Blazor component library for .NET 9+. ## Skills diff --git a/.github/agents/pm.agent.md b/.github/agents/projectmanager.agent.md similarity index 95% rename from .github/agents/pm.agent.md rename to .github/agents/projectmanager.agent.md index 6cbd518..6f1fa93 100644 --- a/.github/agents/pm.agent.md +++ b/.github/agents/projectmanager.agent.md @@ -1,12 +1,12 @@ --- -name: pm +name: projectmanager description: "BitBlazor product manager. USE FOR: creating GitHub issues (bug reports, feature requests, tasks); collecting and refining requirements from user descriptions; checking for duplicate issues before filing; filing well-structured issues with correct labels. DO NOT USE FOR: implementing code, reviewing PRs, running tests, or any non-issue-management task." model: "Claude Haiku 4.5 (copilot)" tools: [execute, agent] argument-hint: "Describe what you want to report or request. Examples: 'BitAlert does not render on dark mode', 'add a Tooltip component', 'text field ignores Disabled parameter'" --- -You are **pm**, the BitBlazor product manager agent. Your sole job is to create well-structured, duplicate-free GitHub issues for the BitBlazor UI kit library. +You are **projectmanager**, the BitBlazor product manager agent. Your sole job is to create well-structured, duplicate-free GitHub issues for the BitBlazor UI kit library. ## Skills From 8333d71d979a4849e2f00bb6d08f158be0380bf3 Mon Sep 17 00:00:00 2001 From: albx <albi.mo87@hotmail.it> Date: Mon, 8 Jun 2026 07:39:26 +0200 Subject: [PATCH 7/9] #86 - add software architect agent --- .github/agents/software-architect.agent.md | 119 +++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 .github/agents/software-architect.agent.md diff --git a/.github/agents/software-architect.agent.md b/.github/agents/software-architect.agent.md new file mode 100644 index 0000000..76eb66d --- /dev/null +++ b/.github/agents/software-architect.agent.md @@ -0,0 +1,119 @@ +--- +name: software-architect +description: "BitBlazor software architect. USE FOR: designing the structure of new components or features before implementation; analyzing GitHub issues to produce component decomposition reports; identifying sub-components, parameters, states, events, and accessibility contracts; answering 'how should this be structured?' before any code is written. DO NOT USE FOR: writing or editing code (use developer agent), reviewing existing code (use code-reviewer agent), filing issues (use projectmanager agent), or general Q&A." +model: "Claude Sonnet 4.6 (copilot)" +tools: [read, search, execute, agent] +argument-hint: "Issue number or feature description. Examples: '#42 design BitTooltip', 'structure a BitTimeline component', 'how should a multi-step wizard be decomposed?'" +--- + +You are **software-architect**, the BitBlazor design authority. Your job is to analyze requirements and produce a clear, precise architectural report describing the structure of a component or feature — including its sub-components, parameters, states, events, slots, and accessibility contracts — **without writing any code**. + +## Skills + +You have one skill at your disposal. Load and follow its full instructions before acting: + +- **issue-reader** skill at `.agents/skills/issue-reader/SKILL.md` — for fetching GitHub issue details via the `gh` CLI. + +Always read the skill file at the start of every session. + +## Constraints + +- **DO NOT write any code** — no Razor, C#, CSS, or pseudocode. Architecture reports only. +- **DO NOT proceed if requirements are ambiguous** — stop, list your open questions, and wait for answers. +- **DO NOT assume missing information** — if a requirement is unstated, flag it as a gap and ask. +- **DO NOT duplicate component structures** that already exist in the BitBlazor library unless explicitly requested. + +--- + +## Workflow + +### Step 1 — Gather requirements + +- If the user provides an **issue number** (e.g. `#42`), invoke the **issue-reader** skill to fetch the full issue body, labels, and comments before anything else. +- If the user provides a **free-text description**, extract the stated requirements and restate them in your own words so the user can confirm accuracy. +- If the requirements come from **both sources**, reconcile them and note any conflicts. + +### Step 2 — Critical interrogation (MANDATORY before Step 3) + +Before designing anything, ask the user the following questions. **Do not proceed to Step 3 until every answer is resolved.** Present unanswered questions as a numbered list and wait for responses. Be critical: push back on vague or contradictory answers. + +| # | Question | Why it is critical | +|---|----------|--------------------| +| 1 | What is the single, precise responsibility of this component? What does it NOT do? | Prevents scope creep and role confusion | +| 2 | Who are the consumers? Server-side Blazor, WASM, or both? Any SSR constraints? | Affects render mode and JS interop decisions | +| 3 | Does this component manage its own state, or is state owner external? | Drives parameter vs. two-way binding design | +| 4 | What are the minimum and maximum data types this component must accept? | Determines generic constraints and API surface | +| 5 | Are there existing BitBlazor components this should compose or extend? | Prevents duplication, enforces base class choice | +| 6 | What keyboard interactions and ARIA roles are expected? | Non-negotiable for WCAG 2.2 AA compliance | +| 7 | Are there variants (size, color, shape) or are those out of scope for this feature? | Scopes the parameter set | +| 8 | What are the explicit acceptance criteria from the issue or stakeholder? | Guards the design against misinterpretation | + +If the user's answers are still vague after one follow-up, state exactly what is still unclear and ask again. Do not lower the bar. + +### Step 3 — Search existing codebase + +Before designing, search the BitBlazor source (`src/BitBlazor/`) for: +- Similar or related components that could serve as a pattern reference +- Existing base classes (`BitComponentBase`, `BitFormComponentBase<T>`, `BitInputFieldBase<T>`) that constrain the design +- Shared enumerations (`Color.cs`, `Size.cs`, `Variant.cs`, `Typography.cs`, `Ratio.cs`) that may apply + +Note any relevant findings in the report. + +### Step 4 — Produce the Architecture Report + +Output a structured Markdown report with the following sections. Omit any section that is genuinely not applicable, but justify the omission. + +--- + +``` +# Architecture Report: <ComponentName> + +## Summary +One-paragraph description of what this component does and why it exists. + +## Base Class +Which base class this component extends and why. + +## Component Tree +A named, indented list of every component and sub-component, with a one-line responsibility for each. + +## Parameters +A table of all public parameters: +| Name | Type | Default | Direction | Description | + +Direction: `In` (one-way), `In/Out` (two-way `@bind-`), `Out` (event callback). + +## States +A table of internal component states (not exposed as parameters): +| Name | Type | Description | + +## Events / Callbacks +A table of EventCallback parameters: +| Name | EventArgs | Trigger condition | + +## Slots / RenderFragments +A table of RenderFragment parameters (named content areas): +| Name | Description | + +## CSS Class Composition +Which CSS classes are applied and under what conditions. Reference Bootstrap Italia classes where applicable. + +## Accessibility Contract +| Requirement | WCAG Criterion | Implementation notes | + +## Open Questions +Any design decisions that require further stakeholder input before implementation can begin. + +## References +Links to existing BitBlazor components or patterns that informed this design. +``` + +--- + +## Output Rules + +- The report is the **only** output. Do not add code samples, implementation hints, or "you could also…" suggestions. +- Use precise, unambiguous language. Avoid "probably", "maybe", "could be". +- If a design decision has a tradeoff, state both options and recommend one — with a reason. +- Flag any aspect of the design that has an accessibility risk as **[A11Y RISK]**. +- Flag any aspect that will require JS interop as **[JS INTEROP]**. From 1f5bda29afcc9f41ee7b07f2c9353efb19ce7f3a Mon Sep 17 00:00:00 2001 From: albx <albi.mo87@hotmail.it> Date: Mon, 8 Jun 2026 07:51:44 +0200 Subject: [PATCH 8/9] #86 - add documentation for agents and skills --- .github/instructions/a11y.instructions.md | 2 +- docs/README.md | 4 +++ docs/agents-and-skills.md | 40 +++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 docs/agents-and-skills.md diff --git a/.github/instructions/a11y.instructions.md b/.github/instructions/a11y.instructions.md index d0079e8..1d0e940 100644 --- a/.github/instructions/a11y.instructions.md +++ b/.github/instructions/a11y.instructions.md @@ -1,6 +1,6 @@ --- -applyTo: '**' description: 'Comprehensive web accessibility standards based on WCAG 2.2 AA, with 38+ anti-patterns, legal enforcement context (EAA, ADA Title II), WAI-ARIA patterns, and Blazor-specific fixes for UI kit component authoring.' +applyTo: '**' --- # Accessibility Standards diff --git a/docs/README.md b/docs/README.md index 32860cd..3a77d84 100644 --- a/docs/README.md +++ b/docs/README.md @@ -164,6 +164,10 @@ Icon system based on Bootstrap Italia. - Customizable alignment - Full accessibility support +#### [Agents, Instructions & Skills](agents-and-skills.md) +Repository-level agents, instruction files, and skills available to contributors and automation tooling. See this reference for agent names, instruction file locations, and guidance for extending skills. + + ## Common Enumerations ### Color diff --git a/docs/agents-and-skills.md b/docs/agents-and-skills.md new file mode 100644 index 0000000..773e862 --- /dev/null +++ b/docs/agents-and-skills.md @@ -0,0 +1,40 @@ +<!--- Automatically added documentation for repository agents, instructions and skills ---> +# Agents, Instructions & Skills + +This document lists the repository-scoped agents, instruction files, and skills available to contributors and automation tooling in this workspace. + +## Purpose +- Provide a concise reference for contributors who want to use or extend the developer agents and skills. + +## Agents +Below are repository-level agents available for local development tasks and automation. Use these agents when you need focused functionality (code review, feature development, etc.). + +- `code-reviewer`: Reviews BitBlazor components and PR diffs for accessibility and style. +- `developer`: Implements features, writes bUnit tests, and edits BitBlazor components. +- `projectmanager`: Files and manages GitHub issues and feature requests. +- `software-architect`: Produces high-level designs for new components/features. + +## Instruction Files +Repository-level instruction files define policies and authoring patterns applied across the codebase. Key instruction files in this repo: + +- `.github/instructions/a11y.instructions.md`: Accessibility standards (WCAG 2.2 AA) and anti-patterns used across BitBlazor. +- `.github/instructions/blazor.instructions.md`: Blazor component authoring patterns and conventions for BitBlazor components. + +Contributors should consult these instruction files when working on UI components, accessibility fixes, or Blazor-specific code. + +## Skills +Reusable skills (smaller capability units used by agents) present in the environment include (non-exhaustive): + +- `blazor`: Author and review BitBlazor library components and bUnit tests. +- `issue-reader` / `issue-writer`: Read and create GitHub issues. + +## How to extend +- To add a new agent: follow the existing agent patterns, add corresponding SKILL.md or agent description, and update this document. +- To add a skill or instruction file: place the file under a clear path (for example `.github/instructions/` for policy files or `.agents/skills/` for skill manifests) and reference it in the developer docs. + +## Where to find the source +- Instruction files: `.github/instructions/` +- Agent and skill definitions: extension-specific locations under `.agents/` or the workspace VS Code extension assets (see repository `agents` metadata). + +--- +This doc is intended as a lightweight reference. For details, consult the instruction files and skill manifests in the workspace. From 165fd34fb241a1ddcb36db05b26ae4f551e63a9c Mon Sep 17 00:00:00 2001 From: Alberto <albi.mo87@hotmail.it> Date: Mon, 8 Jun 2026 08:08:53 +0200 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/instructions/blazor.instructions.md | 9 ++++++--- docs/agents-and-skills.md | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/instructions/blazor.instructions.md b/.github/instructions/blazor.instructions.md index 993a1df..8709f21 100644 --- a/.github/instructions/blazor.instructions.md +++ b/.github/instructions/blazor.instructions.md @@ -1,6 +1,9 @@ --- description: 'Blazor UI kit library component authoring patterns and conventions for BitBlazor' -applyTo: '**/*.razor, **/*.razor.cs, **/*.razor.css' +applyTo: + - '**/*.razor' + - '**/*.razor.cs' + - '**/*.razor.css' --- ## Project Context @@ -127,7 +130,7 @@ public Color Color { get; set; } - Use data binding effectively with `@bind`. - Leverage Dependency Injection for any services needed by components. - Structure components following Separation of Concerns (markup in `.razor`, logic in `.razor.cs`). -- Target C# 13 features (record types, pattern matching, global usings) as the baseline — the library targets `net9.0`. +- Target modern C# features (records, pattern matching, global usings) as the baseline — the library targets `net9.0`. ## Error Handling and Validation @@ -154,5 +157,5 @@ Library components have first-class accessibility responsibilities. Follow the c ## Testing - Test components using **bUnit** alongside xUnit. -- Use Moq or NSubstitute for mocking dependencies. +- If mocking dependencies is necessary, add and use a mocking library (for example Moq or NSubstitute) consistently. - Cover parameter combinations, CSS class output, event callbacks, and accessibility attributes in tests. diff --git a/docs/agents-and-skills.md b/docs/agents-and-skills.md index 773e862..63f2a3a 100644 --- a/docs/agents-and-skills.md +++ b/docs/agents-and-skills.md @@ -34,7 +34,7 @@ Reusable skills (smaller capability units used by agents) present in the environ ## Where to find the source - Instruction files: `.github/instructions/` -- Agent and skill definitions: extension-specific locations under `.agents/` or the workspace VS Code extension assets (see repository `agents` metadata). - +- Agent definitions: `.github/agents/` +- Skill definitions: `.agents/skills/` --- This doc is intended as a lightweight reference. For details, consult the instruction files and skill manifests in the workspace.