UI Widget Creation Skill
This skill defines the process for adding new reusable UI widgets to the
packages/app_ui/ package.
[!NOTE]
For details on mapping Figma designs, typography, HSL colors, spacing,
and asset generation, refer to the Design-to-Code Skill.
1. Directory Structure
Widgets must be placed in packages/app_ui/lib/src/widgets/ by category:
packages/app_ui/lib/src/widgets/
├── buttons/
│ └── primary_button.dart # Class name: YzPrimaryButton
├── layout/
│ └── web/
│ └── scaffold_web.dart # Class name: YzScaffoldWeb
2. Implementation Rules
Follow these rules when implementing a widget in app_ui:
- Naming: Class names must prefix with
Yz (e.g. YzPrimaryButton). File
names must be snake_case without yz_ prefix (e.g. primary_button.dart).
- Generic Naming: Always use generic, domain-agnostic names for widgets. For example, name a button
YzPrimaryButton rather than YzLoginButton, even if it is currently only used on a specific screen.
- Web suffix: If a web variant exists for a shared concept, append
Web
(e.g., YzScaffoldWeb).
- No Helper Functions: Do not use helper methods like
_buildHeader().
Split components into private classes (e.g., _Header) within the same
file (this applies to widgets inside app_ui, NOT feature views).
- Externalize Text: Never hardcode user-facing strings in
app_ui. Always
pass them as parameters so the feature views can handle translations.
- Configuration Models: If a widget has complex parameters, create a
dedicated config class in the same file (e.g.,
YzPanelConfig).
2.1 Example Implementation
import 'package:flutter/material.dart';
/// Content card for displaying simple text.
///
/// ---
/// @UI: Card | Content
/// @Source: — | Status: manual
/// @Style: Bg: surface | Radius: md
/// @Layout: Padding: md
class YzCard extends StatelessWidget {
const YzCard({
required this.title,
required this.onTap,
super.key,
});
final String title;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(AppSpacing.md),
child: Text(title),
),
);
}
}
3. Exports
Always export your new widget in the root library files:
- Mobile/Shared widgets:
packages/app_ui/lib/app_ui.dart
- Web-specific widgets:
packages/app_ui/lib/app_ui_web.dart
4. Widget Annotations (Code-as-Truth)
We do not use a centralized registry.md. Instead, every reusable widget must include a strict 4-line doc-comment block directly above the class definition. This allows AI agents to rapidly filter and deduplicate widgets across the entire codebase using grep_search.
The Annotation Schema
The block must be separated by a horizontal rule (---) and consists of 4 grouped @ tags:
@UI: Category and Intent (e.g., Button | Primary CTA).
@Source: Figma breadcrumb and sync status (e.g., Login > Hero > CTA | Status: synced).
@Style: Colors and shapes (Bg, Text, Radius, Border, Shadow).
@Layout: Spacing and structure (Padding, Size, Icon).
Rules
- Omit Defaults: To keep the footprint minimal, only include properties that are actively styled. If a widget has no border or shadow, do not write
Border: none. Omit them entirely.
- 80-Character Limit: The grouped lines must not exceed 80 characters. If a line is too long, wrap it to a new line with the same tag prefix.
Dictionary Keys
Use exact keys and tokens (e.g., Bg: primary | Radius: pill):
Bg: Semantic color (e.g., primary, surface, transparent)
Text: Typography token (e.g., labelLarge, bodyMedium)
Radius: Shape (e.g., sm, md, pill)
Border: Outline style (e.g., outline, error)
Shadow: Elevation (e.g., sm, md)
Padding: Spacing (e.g., sm, md, lg)
Size: Layout behavior (e.g., expanded, shrink, fixed)
Icon: Presence (e.g., leading, trailing, only)
1---2name: create-ui-widget3description: Process for building, exporting, and registering reusable UI components in the packages/app_ui package. References design-to-code.md for design tokens (typography, colors, spacing).4---56# UI Widget Creation Skill78This skill defines the process for adding new reusable UI widgets to the9`packages/app_ui/` package.1011> [!NOTE]12> For details on mapping Figma designs, typography, HSL colors, spacing,13> and asset generation, refer to the [Design-to-Code Skill](../design-to-code/SKILL.md).1415## 1. Directory Structure1617Widgets must be placed in `packages/app_ui/lib/src/widgets/` by category:1819```text20packages/app_ui/lib/src/widgets/21├── buttons/22│ └── primary_button.dart # Class name: YzPrimaryButton23├── layout/24│ └── web/25│ └── scaffold_web.dart # Class name: YzScaffoldWeb26```2728## 2. Implementation Rules2930Follow these rules when implementing a widget in `app_ui`:3132- **Naming**: Class names must prefix with `Yz` (e.g. `YzPrimaryButton`). File33 names must be `snake_case` without `yz_` prefix (e.g. `primary_button.dart`).34- **Generic Naming**: Always use generic, domain-agnostic names for widgets. For example, name a button `YzPrimaryButton` rather than `YzLoginButton`, even if it is currently only used on a specific screen.35- **Web suffix**: If a web variant exists for a shared concept, append `Web`36 (e.g., `YzScaffoldWeb`).37- **No Helper Functions**: Do not use helper methods like `_buildHeader()`.38 Split components into **private classes** (e.g., `_Header`) within the same39 file (this applies to widgets inside `app_ui`, NOT feature views).40- **Externalize Text**: Never hardcode user-facing strings in `app_ui`. Always41 pass them as parameters so the feature views can handle translations.42- **Configuration Models**: If a widget has complex parameters, create a43 dedicated config class in the same file (e.g., `YzPanelConfig`).4445### 2.1 Example Implementation4647```dart48import 'package:flutter/material.dart';4950/// Content card for displaying simple text.51///52/// ---53/// @UI: Card | Content54/// @Source: — | Status: manual55/// @Style: Bg: surface | Radius: md56/// @Layout: Padding: md57class YzCard extends StatelessWidget {58 const YzCard({59 required this.title,60 required this.onTap,61 super.key,62 });6364 final String title;65 final VoidCallback onTap;6667 @override68 Widget build(BuildContext context) {69 return GestureDetector(70 onTap: onTap,71 child: Container(72 padding: const EdgeInsets.all(AppSpacing.md),73 child: Text(title),74 ),75 );76 }77}78```7980## 3. Exports8182Always export your new widget in the root library files:83- Mobile/Shared widgets: `packages/app_ui/lib/app_ui.dart`84- Web-specific widgets: `packages/app_ui/lib/app_ui_web.dart`8586## 4. Widget Annotations (Code-as-Truth)8788We do **not** use a centralized `registry.md`. Instead, every reusable widget must include a strict 4-line doc-comment block directly above the class definition. This allows AI agents to rapidly filter and deduplicate widgets across the entire codebase using `grep_search`.8990### The Annotation Schema9192The block must be separated by a horizontal rule (`---`) and consists of 4 grouped `@` tags:93941. `@UI:` Category and Intent (e.g., `Button | Primary CTA`).952. `@Source:` Figma breadcrumb and sync status (e.g., `Login > Hero > CTA | Status: synced`).963. `@Style:` Colors and shapes (`Bg`, `Text`, `Radius`, `Border`, `Shadow`).974. `@Layout:` Spacing and structure (`Padding`, `Size`, `Icon`).9899### Rules1001011. **Omit Defaults**: To keep the footprint minimal, only include properties that are actively styled. If a widget has no border or shadow, do not write `Border: none`. Omit them entirely.1022. **80-Character Limit**: The grouped lines must not exceed 80 characters. If a line is too long, wrap it to a new line with the same tag prefix.103104### Dictionary Keys105106Use exact keys and tokens (e.g., `Bg: primary | Radius: pill`):107- `Bg`: Semantic color (e.g., `primary`, `surface`, `transparent`)108- `Text`: Typography token (e.g., `labelLarge`, `bodyMedium`)109- `Radius`: Shape (e.g., `sm`, `md`, `pill`)110- `Border`: Outline style (e.g., `outline`, `error`)111- `Shadow`: Elevation (e.g., `sm`, `md`)112- `Padding`: Spacing (e.g., `sm`, `md`, `lg`)113- `Size`: Layout behavior (e.g., `expanded`, `shrink`, `fixed`)114- `Icon`: Presence (e.g., `leading`, `trailing`, `only`)