mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-19 18:28:42 +00:00
Update LLM instructions to recent codebase changes (#28017)
* Update dialog instructions * Add button styling and dialog sizing * Document spacing tokens * Format * Explicit spacing token documentation * View transitions * Add vt notes * Keyboard shortcuts * Tooltip * Link to the gallery for guidelines * Add AGENTS.md symlink for newer tools * Use more sane spacing tokens
This commit is contained in:
183
.github/copilot-instructions.md
vendored
183
.github/copilot-instructions.md
vendored
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
You are an assistant helping with development of the Home Assistant frontend. The frontend is built using Lit-based Web Components and TypeScript, providing a responsive and performant interface for home automation control.
|
You are an assistant helping with development of the Home Assistant frontend. The frontend is built using Lit-based Web Components and TypeScript, providing a responsive and performant interface for home automation control.
|
||||||
|
|
||||||
|
**Note**: This file contains high-level guidelines and references to implementation patterns. For detailed component documentation, API references, and usage examples, refer to the `gallery/` directory.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Quick Reference](#quick-reference)
|
- [Quick Reference](#quick-reference)
|
||||||
@@ -151,6 +153,10 @@ try {
|
|||||||
### Styling Guidelines
|
### Styling Guidelines
|
||||||
|
|
||||||
- **Use CSS custom properties**: Leverage the theme system
|
- **Use CSS custom properties**: Leverage the theme system
|
||||||
|
- **Use spacing tokens**: Prefer `--ha-space-*` tokens over hardcoded values for consistent spacing
|
||||||
|
- Spacing scale: `--ha-space-0` (0px) through `--ha-space-20` (80px) in 4px increments
|
||||||
|
- Defined in `src/resources/theme/core.globals.ts`
|
||||||
|
- Common values: `--ha-space-2` (8px), `--ha-space-4` (16px), `--ha-space-8` (32px)
|
||||||
- **Mobile-first responsive**: Design for mobile, enhance for desktop
|
- **Mobile-first responsive**: Design for mobile, enhance for desktop
|
||||||
- **Follow Material Design**: Use Material Web Components where appropriate
|
- **Follow Material Design**: Use Material Web Components where appropriate
|
||||||
- **Support RTL**: Ensure all layouts work in RTL languages
|
- **Support RTL**: Ensure all layouts work in RTL languages
|
||||||
@@ -159,21 +165,68 @@ try {
|
|||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
--spacing: 16px;
|
padding: var(--ha-space-4);
|
||||||
padding: var(--spacing);
|
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
background-color: var(--card-background-color);
|
background-color: var(--card-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
gap: var(--ha-space-2);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
:host {
|
:host {
|
||||||
--spacing: 8px;
|
padding: var(--ha-space-2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### View Transitions
|
||||||
|
|
||||||
|
The View Transitions API creates smooth animations between DOM state changes. When implementing view transitions:
|
||||||
|
|
||||||
|
**Core Resources:**
|
||||||
|
|
||||||
|
- **Utility wrapper**: `src/common/util/view-transition.ts` - `withViewTransition()` function with graceful fallback
|
||||||
|
- **Real-world example**: `src/util/launch-screen.ts` - Launch screen fade pattern with browser support detection
|
||||||
|
- **Animation keyframes**: `src/resources/theme/animations.globals.ts` - Global `fade-in`, `fade-out`, `scale` animations
|
||||||
|
- **Animation duration**: `src/resources/theme/core.globals.ts` - `--ha-animation-base-duration` (350ms, respects `prefers-reduced-motion`)
|
||||||
|
|
||||||
|
**Implementation Guidelines:**
|
||||||
|
|
||||||
|
1. Always use `withViewTransition()` wrapper for automatic fallback
|
||||||
|
2. Keep transitions simple (subtle crossfades and fades work best)
|
||||||
|
3. Use `--ha-animation-base-duration` CSS variable for consistent timing
|
||||||
|
4. Assign unique `view-transition-name` to elements (must be unique at any given time)
|
||||||
|
5. For Lit components: Override `performUpdate()` or use `::part()` for internal elements
|
||||||
|
|
||||||
|
**Default Root Transition:**
|
||||||
|
|
||||||
|
By default, `:root` receives `view-transition-name: root`, creating a full-page crossfade. Target with [`::view-transition-group(root)`](https://developer.mozilla.org/en-US/docs/Web/CSS/::view-transition-group) to customize the default page transition.
|
||||||
|
|
||||||
|
**Important Constraints:**
|
||||||
|
|
||||||
|
- Each `view-transition-name` must be unique at any given time
|
||||||
|
- Only one view transition can run at a time
|
||||||
|
- **Shadow DOM incompatibility**: View transitions operate at document level and do not work within Shadow DOM due to style isolation ([spec discussion](https://github.com/w3c/csswg-drafts/issues/10303)). For web components, set `view-transition-name` on the `:host` element or use document-level transitions
|
||||||
|
|
||||||
|
**Current Usage & Planned Applications:**
|
||||||
|
|
||||||
|
- Launch screen fade out (implemented)
|
||||||
|
- Automation sidebar transitions (planned - #27238)
|
||||||
|
- More info dialog content changes (planned - #27672)
|
||||||
|
- Toolbar navigation, ha-spinner transitions (planned)
|
||||||
|
|
||||||
|
**Specification & Documentation:**
|
||||||
|
|
||||||
|
For browser support, API details, and current specifications, refer to these authoritative sources (note: check publication dates as specs evolve):
|
||||||
|
|
||||||
|
- [MDN: View Transition API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API) - Comprehensive API reference
|
||||||
|
- [Chrome for Developers: View Transitions](https://developer.chrome.com/docs/web-platform/view-transitions) - Implementation guide and examples
|
||||||
|
- [W3C Draft Specification](https://drafts.csswg.org/css-view-transitions/) - Official specification (evolving)
|
||||||
|
|
||||||
### Performance Best Practices
|
### Performance Best Practices
|
||||||
|
|
||||||
- **Code split**: Split code at the panel/dialog level
|
- **Code split**: Split code at the panel/dialog level
|
||||||
@@ -195,8 +248,9 @@ static get styles() {
|
|||||||
|
|
||||||
**Available Dialog Types:**
|
**Available Dialog Types:**
|
||||||
|
|
||||||
- `ha-md-dialog` - Preferred for new code (Material Design 3)
|
- `ha-wa-dialog` - Preferred for new dialogs (Web Awesome based)
|
||||||
- `ha-dialog` - Legacy component still widely used
|
- `ha-md-dialog` - Material Design 3 dialog component
|
||||||
|
- `ha-dialog` - Legacy component (still widely used)
|
||||||
|
|
||||||
**Opening Dialogs (Fire Event Pattern - Recommended):**
|
**Opening Dialogs (Fire Event Pattern - Recommended):**
|
||||||
|
|
||||||
@@ -211,15 +265,45 @@ fireEvent(this, "show-dialog", {
|
|||||||
**Dialog Implementation Requirements:**
|
**Dialog Implementation Requirements:**
|
||||||
|
|
||||||
- Implement `HassDialog<T>` interface
|
- Implement `HassDialog<T>` interface
|
||||||
- Use `createCloseHeading()` for standard headers
|
- Use `@state() private _open = false` to control dialog visibility
|
||||||
- Import `haStyleDialog` for consistent styling
|
- Set `_open = true` in `showDialog()`, `_open = false` in `closeDialog()`
|
||||||
- Return `nothing` when no params (loading state)
|
- Return `nothing` when no params (loading state)
|
||||||
- Fire `dialog-closed` event when closing
|
- Fire `dialog-closed` event in `_dialogClosed()` handler
|
||||||
- Add `dialogInitialFocus` for accessibility
|
- Use `header-title` attribute for simple titles
|
||||||
|
- Use `header-subtitle` attribute for simple subtitles
|
||||||
|
- Use slots for custom content where the standard attributes are not enough
|
||||||
|
- Use `ha-dialog-footer` with `primaryAction`/`secondaryAction` slots for footer content
|
||||||
|
- Add `autofocus` to first focusable element (e.g., `<ha-form autofocus>`). The component may need to forward this attribute internally.
|
||||||
|
|
||||||
````
|
**Dialog Sizing:**
|
||||||
|
|
||||||
|
- Use `width` attribute with predefined sizes: `"small"` (320px), `"medium"` (560px - default), `"large"` (720px), or `"full"`
|
||||||
|
- Custom sizing is NOT recommended - use the standard width presets
|
||||||
|
- Example: `<ha-wa-dialog width="small">` for alert/confirmation dialogs
|
||||||
|
|
||||||
|
**Button Appearance Guidelines:**
|
||||||
|
|
||||||
|
- **Primary action buttons**: Default appearance (no appearance attribute) or omit for standard styling
|
||||||
|
- **Secondary action buttons**: Use `appearance="plain"` for cancel/dismiss actions
|
||||||
|
- **Destructive actions**: Use `appearance="filled"` for delete/remove operations (combined with appropriate semantic styling)
|
||||||
|
- **Button sizes**: Use `size="small"` (32px height) or default/medium (40px height)
|
||||||
|
- Always place primary action in `slot="primaryAction"` and secondary in `slot="secondaryAction"` within `ha-dialog-footer`
|
||||||
|
|
||||||
|
**Recent Examples:**
|
||||||
|
|
||||||
|
See these files for current patterns:
|
||||||
|
|
||||||
|
- `src/panels/config/repairs/dialog-repairs-issue.ts`
|
||||||
|
- `src/dialogs/restart/dialog-restart.ts`
|
||||||
|
- `src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts`
|
||||||
|
|
||||||
|
**Gallery Documentation:**
|
||||||
|
|
||||||
|
- `gallery/src/pages/components/ha-wa-dialog.markdown`
|
||||||
|
- `gallery/src/pages/components/ha-dialogs.markdown`
|
||||||
|
|
||||||
### Form Component (ha-form)
|
### Form Component (ha-form)
|
||||||
|
|
||||||
- Schema-driven using `HaFormSchema[]`
|
- Schema-driven using `HaFormSchema[]`
|
||||||
- Supports entity, device, area, target, number, boolean, time, action, text, object, select, icon, media, location selectors
|
- Supports entity, device, area, target, number, boolean, time, action, text, object, select, icon, media, location selectors
|
||||||
- Built-in validation with error display
|
- Built-in validation with error display
|
||||||
@@ -235,7 +319,11 @@ fireEvent(this, "show-dialog", {
|
|||||||
.computeLabel=${(schema) => this.hass.localize(`ui.panel.${schema.name}`)}
|
.computeLabel=${(schema) => this.hass.localize(`ui.panel.${schema.name}`)}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
````
|
```
|
||||||
|
|
||||||
|
**Gallery Documentation:**
|
||||||
|
|
||||||
|
- `gallery/src/pages/components/ha-form.markdown`
|
||||||
|
|
||||||
### Alert Component (ha-alert)
|
### Alert Component (ha-alert)
|
||||||
|
|
||||||
@@ -249,6 +337,35 @@ fireEvent(this, "show-dialog", {
|
|||||||
<ha-alert alert-type="success" dismissable>Success message</ha-alert>
|
<ha-alert alert-type="success" dismissable>Success message</ha-alert>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Gallery Documentation:**
|
||||||
|
|
||||||
|
- `gallery/src/pages/components/ha-alert.markdown`
|
||||||
|
|
||||||
|
### Keyboard Shortcuts (ShortcutManager)
|
||||||
|
|
||||||
|
The `ShortcutManager` class provides a unified way to register keyboard shortcuts with automatic input field protection.
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
|
||||||
|
- Automatically blocks shortcuts when input fields are focused
|
||||||
|
- Prevents shortcuts during text selection (configurable via `allowWhenTextSelected`)
|
||||||
|
- Supports both character-based and KeyCode-based shortcuts (for non-latin keyboards)
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
|
||||||
|
- **Class definition**: `src/common/keyboard/shortcuts.ts`
|
||||||
|
- **Real-world example**: `src/state/quick-bar-mixin.ts` - Global shortcuts (e, c, d, m, a, Shift+?) with non-latin keyboard fallbacks
|
||||||
|
|
||||||
|
### Tooltip Component (ha-tooltip)
|
||||||
|
|
||||||
|
The `ha-tooltip` component wraps Web Awesome tooltip with Home Assistant theming. Use for providing contextual help text on hover.
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
|
||||||
|
- **Component definition**: `src/components/ha-tooltip.ts`
|
||||||
|
- **Usage example**: `src/components/ha-label.ts`
|
||||||
|
- **Gallery documentation**: `gallery/src/pages/components/ha-tooltip.markdown`
|
||||||
|
|
||||||
## Common Patterns
|
## Common Patterns
|
||||||
|
|
||||||
### Creating a Panel
|
### Creating a Panel
|
||||||
@@ -289,11 +406,19 @@ export class DialogMyFeature
|
|||||||
@state()
|
@state()
|
||||||
private _params?: MyDialogParams;
|
private _params?: MyDialogParams;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _open = false;
|
||||||
|
|
||||||
public async showDialog(params: MyDialogParams): Promise<void> {
|
public async showDialog(params: MyDialogParams): Promise<void> {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
|
this._open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
|
this._open = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dialogClosed(): void {
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
@@ -304,23 +429,27 @@ export class DialogMyFeature
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-wa-dialog
|
||||||
open
|
.hass=${this.hass}
|
||||||
@closed=${this.closeDialog}
|
.open=${this._open}
|
||||||
.heading=${createCloseHeading(this.hass, this._params.title)}
|
header-title=${this._params.title}
|
||||||
|
header-subtitle=${this._params.subtitle}
|
||||||
|
@closed=${this._dialogClosed}
|
||||||
>
|
>
|
||||||
<!-- Dialog content -->
|
<p>Dialog content</p>
|
||||||
<ha-button
|
<ha-dialog-footer slot="footer">
|
||||||
appearance="plain"
|
<ha-button
|
||||||
@click=${this.closeDialog}
|
slot="secondaryAction"
|
||||||
slot="secondaryAction"
|
appearance="plain"
|
||||||
>
|
@click=${this.closeDialog}
|
||||||
${this.hass.localize("ui.common.cancel")}
|
>
|
||||||
</ha-button>
|
${this.hass.localize("ui.common.cancel")}
|
||||||
<ha-button @click=${this._submit} slot="primaryAction">
|
</ha-button>
|
||||||
${this.hass.localize("ui.common.save")}
|
<ha-button slot="primaryAction" @click=${this._submit}>
|
||||||
</ha-button>
|
${this.hass.localize("ui.common.save")}
|
||||||
</ha-dialog>
|
</ha-button>
|
||||||
|
</ha-dialog-footer>
|
||||||
|
</ha-wa-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user