From e1a787c1dd6fac637aaf53bb55791afb0ec913fe Mon Sep 17 00:00:00 2001 From: Zakhar Timoshenko Date: Sat, 17 Oct 2026 04:01:58 +0300 Subject: [PATCH] made android client --- .../material-3/.claude-plugin/plugin.json | 18 + .agents/skills/material-3/SKILL.md | 667 ++++++++++++++ .../material-3/references/color-system.md | 358 ++++++++ .../references/component-catalog.md | 836 ++++++++++++++++++ .../references/layout-and-responsive.md | 733 +++++++++++++++ .../references/navigation-patterns.md | 546 ++++++++++++ .../references/theming-and-dynamic-color.md | 484 ++++++++++ .../references/typography-and-shape.md | 400 +++++++++ .dockerignore | 1 + .impeccable/hook.cache.json | 2 +- README.md | 25 + android/.gitignore | 7 + android/app/build.gradle.kts | 70 ++ android/app/proguard-rules.pro | 4 + android/app/src/main/AndroidManifest.xml | 55 ++ .../main/kotlin/ritm/schedule/MainActivity.kt | 315 +++++++ .../main/kotlin/ritm/schedule/ScheduleApp.kt | 25 + .../src/main/kotlin/ritm/schedule/data/Api.kt | 82 ++ .../main/kotlin/ritm/schedule/data/Dates.kt | 48 + .../main/kotlin/ritm/schedule/data/Models.kt | 154 ++++ .../kotlin/ritm/schedule/data/Repository.kt | 55 ++ .../kotlin/ritm/schedule/data/Settings.kt | 121 +++ .../kotlin/ritm/schedule/notify/Reminders.kt | 155 ++++ .../main/kotlin/ritm/schedule/ui/Common.kt | 485 ++++++++++ .../kotlin/ritm/schedule/ui/GroupsScreen.kt | 276 ++++++ .../kotlin/ritm/schedule/ui/HomeScreen.kt | 390 ++++++++ .../kotlin/ritm/schedule/ui/MainViewModel.kt | 91 ++ .../src/main/kotlin/ritm/schedule/ui/Nav.kt | 31 + .../kotlin/ritm/schedule/ui/ScheduleScreen.kt | 631 +++++++++++++ .../kotlin/ritm/schedule/ui/SearchScreen.kt | 427 +++++++++ .../kotlin/ritm/schedule/ui/SettingsScreen.kt | 190 ++++ .../main/kotlin/ritm/schedule/ui/Subjects.kt | 102 +++ .../kotlin/ritm/schedule/ui/SubjectsScreen.kt | 362 ++++++++ .../main/kotlin/ritm/schedule/ui/WeekGrid.kt | 206 +++++ .../kotlin/ritm/schedule/ui/theme/Theme.kt | 126 +++ .../ritm/schedule/widget/TodayWidget.kt | 154 ++++ .../kotlin/ritm/schedule/work/SyncWorker.kt | 56 ++ .../res/drawable/ic_launcher_foreground.xml | 18 + .../src/main/res/drawable/ic_notification.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../app/src/main/res/values-night/colors.xml | 4 + .../app/src/main/res/values-night/themes.xml | 8 + android/app/src/main/res/values/colors.xml | 6 + android/app/src/main/res/values/strings.xml | 7 + android/app/src/main/res/values/themes.xml | 9 + .../main/res/xml/network_security_config.xml | 10 + .../src/main/res/xml/today_widget_info.xml | 11 + android/build.gradle.kts | 5 + android/gradle.properties | 11 + android/gradle/libs.versions.toml | 41 + android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 48462 bytes .../gradle/wrapper/gradle-wrapper.properties | 9 + android/gradlew | 248 ++++++ android/gradlew.bat | 82 ++ android/settings.gradle.kts | 16 + server/index.js | 24 + server/timetable.js | 101 +++ skills-lock.json | 11 + test/timetable.test.js | 53 ++ 59 files changed, 9379 insertions(+), 1 deletion(-) create mode 100644 .agents/skills/material-3/.claude-plugin/plugin.json create mode 100644 .agents/skills/material-3/SKILL.md create mode 100644 .agents/skills/material-3/references/color-system.md create mode 100644 .agents/skills/material-3/references/component-catalog.md create mode 100644 .agents/skills/material-3/references/layout-and-responsive.md create mode 100644 .agents/skills/material-3/references/navigation-patterns.md create mode 100644 .agents/skills/material-3/references/theming-and-dynamic-color.md create mode 100644 .agents/skills/material-3/references/typography-and-shape.md create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/ritm/schedule/MainActivity.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ScheduleApp.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/data/Api.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/data/Dates.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/data/Models.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/data/Repository.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/data/Settings.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/notify/Reminders.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/Common.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/GroupsScreen.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/HomeScreen.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/MainViewModel.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/Nav.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/ScheduleScreen.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/SearchScreen.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/SettingsScreen.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/Subjects.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/SubjectsScreen.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/WeekGrid.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/ui/theme/Theme.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/widget/TodayWidget.kt create mode 100644 android/app/src/main/kotlin/ritm/schedule/work/SyncWorker.kt create mode 100644 android/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 android/app/src/main/res/drawable/ic_notification.xml create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 android/app/src/main/res/values-night/colors.xml create mode 100644 android/app/src/main/res/values-night/themes.xml create mode 100644 android/app/src/main/res/values/colors.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/values/themes.xml create mode 100644 android/app/src/main/res/xml/network_security_config.xml create mode 100644 android/app/src/main/res/xml/today_widget_info.xml create mode 100644 android/build.gradle.kts create mode 100644 android/gradle.properties create mode 100644 android/gradle/libs.versions.toml create mode 100644 android/gradle/wrapper/gradle-wrapper.jar create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100755 android/gradlew create mode 100644 android/gradlew.bat create mode 100644 android/settings.gradle.kts create mode 100644 server/timetable.js create mode 100644 skills-lock.json create mode 100644 test/timetable.test.js diff --git a/.agents/skills/material-3/.claude-plugin/plugin.json b/.agents/skills/material-3/.claude-plugin/plugin.json new file mode 100644 index 0000000..c7f14a7 --- /dev/null +++ b/.agents/skills/material-3/.claude-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "material-3", + "version": "1.1.1", + "description": "Material Design 3 (Material You) implementation skill for Jetpack Compose, Flutter, and web.", + "author": { + "name": "Ivan Morgillo" + }, + "repository": "https://github.com/hamen/material-3-skill", + "license": "MIT", + "keywords": [ + "material-design", + "material3", + "jetpack-compose", + "flutter", + "android", + "ui" + ] +} diff --git a/.agents/skills/material-3/SKILL.md b/.agents/skills/material-3/SKILL.md new file mode 100644 index 0000000..7619bf0 --- /dev/null +++ b/.agents/skills/material-3/SKILL.md @@ -0,0 +1,667 @@ +--- +name: material-3 +description: > + Implement Google's Material Design 3 (Material You) UI system. Primary: Jetpack Compose + Material3 (MaterialTheme, components, adaptive layout). Also Flutter and limited web + (@material/web, maintenance mode). Covers tokens, 30+ components, layout, theming, + M3 Expressive (platform matrix), and accessibility. Use when: "material design", "MD3", + "material you", "Jetpack Compose", "MaterialTheme", "material component", "md3 button". +user-invokable: true +argument-hint: "[component|theme|layout|scaffold|audit] [description or URL]" +--- + +# Material Design 3 + +This skill guides implementation of Google's Material Design 3 (MD3) — a personal, adaptive, expressive design system. MD3 uses dynamic color, tonal surfaces, rounded shapes, and spring-based motion to create UIs that feel alive and personal. + +## Philosophy + +MD3 is built on three principles: +- **Personal**: Dynamic color adapts UI to the user's wallpaper or content. Theming is individual, not one-size-fits-all. +- **Adaptive**: Layouts transform across 5 window size classes. Components resize, reposition, and change form factor responsively. +- **Expressive**: Shape morphing, spring physics, and emphasized typography create moments of delight without sacrificing usability. + +## Current Updates: Google I/O 2026 + +Material's [Google I/O 2026 update](https://m3.material.io/blog/whats-new-at-io26) reinforces a **Compose-first** Android path and expands expressive/adaptive guidance: + +- **Material Android is Compose-first**: For new Android work, prefer Jetpack Compose Material3 for the latest components, expressive APIs, adaptive scaffolds, and Styles API integration. Android Views may remain necessary in existing apps, but they should not be treated as the default path for new Material 3 implementations. +- **Expressive layout system**: Use an expressive layout scaffold to adapt screens across mobile, desktop, foldables, watches, XR, and other spatial form factors. Start from adaptive scaffolds/window size classes instead of fixed phone-first layouts. +- **8dp spacing system**: Apply spacing tokens for margins, padding, and gaps so layouts and components can adapt programmatically to device type and density. +- **New/updated expressive components**: Lists, menus, search, and search app bars have refreshed expressive guidance, with Jetpack Compose as the primary implementation target. +- **Watches and XR**: Watches emphasize physics-based motion, arc text, and edge-hugging containers. XR emphasizes spatial panels and depth-based elevation. + +**Key differences from MD2:** +- Tonal surfaces replace elevation shadows as the primary depth cue +- Dynamic color generates full schemes from a single seed color +- Fully rounded corners by default (not slightly rounded) +- Spring-based motion physics replace fixed easing curves for components +- 3 levels of user-controlled contrast (standard/medium/high) + +**Relationship with frontend-design skill:** +When both skills are active, MD3 provides the design system (tokens, components, layout rules) and frontend-design provides creative direction within those constraints. MD3 rules take precedence for component structure and token usage. Note: Roboto/Roboto Flex IS the correct default typeface in MD3 — the frontend-design guidance to avoid Roboto does not apply when implementing MD3. + +## Decision Tree + +**What are you building?** +``` +Full app scaffold → See "Common Patterns: App Shell" + references/layout-and-responsive.md +Single component → See "Component Quick Reference" table → references/component-catalog.md +Custom theme → See references/theming-and-dynamic-color.md +Form / input layout → See references/component-catalog.md § Input Components +Navigation structure → See references/navigation-patterns.md +Data display → See references/component-catalog.md § Data Display +``` + +**What platform?** +``` +Jetpack Compose → Primary: androidx.compose.material3, MaterialTheme, references/* +Flutter → useMaterial3: true in ThemeData, ColorScheme.fromSeed() +Web (vanilla JS) → @material/web (limited; maintenance mode) + CSS custom properties +Web (React/Vue/Svelte) → CSS custom properties + wrapper components (no official React lib) +Web (CSS-only) → MD3 token values as CSS custom properties (no elements) +``` + +## Design Token System + +All MD3 tokens use the `md.sys` namespace. **Jetpack Compose** maps roles to `MaterialTheme.colorScheme`, `MaterialTheme.typography`, and `MaterialTheme.shapes` (same semantic roles as the spec). **On the web**, these map to CSS custom properties (`--md-sys-*`): + +### Color Tokens (`--md-sys-color-*`) +| Token | Purpose | +|-------|---------| +| `primary` | High-emphasis fills, text, icons against surface | +| `on-primary` | Text/icons on primary | +| `primary-container` | Standout fill for key components (FAB, etc.) | +| `on-primary-container` | Text/icons on primary-container | +| `secondary` / `on-secondary` | Less prominent accents | +| `secondary-container` / `on-secondary-container` | Recessive components (tonal buttons) | +| `tertiary` / `on-tertiary` | Contrasting accents | +| `tertiary-container` / `on-tertiary-container` | Complementary containers | +| `error` / `on-error` | Error states (static — doesn't change with dynamic color) | +| `error-container` / `on-error-container` | Error container fills | +| `surface` | Default background | +| `on-surface` | Text/icons on any surface | +| `on-surface-variant` | Lower-emphasis text/icons on surface | +| `surface-container-lowest` | Lowest-emphasis container | +| `surface-container-low` | Low-emphasis container | +| `surface-container` | Default container (nav areas) | +| `surface-container-high` | High-emphasis container | +| `surface-container-highest` | Highest-emphasis container | +| `surface-dim` / `surface-bright` | Maintain relative brightness across light/dark | +| `inverse-surface` / `inverse-on-surface` / `inverse-primary` | Contrasting elements (snackbars) | +| `outline` | Important boundaries (text field borders) | +| `outline-variant` | Decorative elements (dividers) | + +Full details: `references/color-system.md` + +### Typography Tokens (`--md-sys-typescale-*`) +| Scale | Sizes | Use | +|-------|-------|-----| +| Display | L / M / S | Hero text, large numbers | +| Headline | L / M / S | Section headers | +| Title | L / M / S | Smaller headers, card titles | +| Body | L / M / S | Paragraph text, descriptions | +| Label | L / M / S | Buttons, chips, captions | + +Each style has tokens for: `-font`, `-weight`, `-size`, `-line-height`, `-tracking` +Plus 15 **emphasized** variants (higher weight) via `--md-sys-typescale-emphasized-*` + +Full details: `references/typography-and-shape.md` + +### Shape Tokens (`--md-sys-shape-corner-*`) +| Token | Value | Example components | +|-------|-------|-------------------| +| `none` | 0dp | — | +| `extra-small` | 4dp | Chips, snackbars | +| `small` | 8dp | Text fields, menus | +| `medium` | 12dp | Cards | +| `large` | 16dp | FABs, navigation drawer | +| `large-increased` | 20dp | (Expressive) | +| `extra-large` | 28dp | Dialogs, bottom sheets | +| `extra-large-increased` | 32dp | (Expressive) | +| `extra-extra-large` | 48dp | (Expressive) | +| `full` | 9999px | Buttons, chips, badges | + +### Elevation Levels +| Level | DP | Tonal offset | Use | +|-------|-----|-------------|-----| +| 0 | 0dp | None | Flat surfaces, most components at rest | +| 1 | 1dp | +5% primary | Elevated cards, modal sheets | +| 2 | 3dp | +8% primary | Menus, nav bar, scrolled app bar | +| 3 | 6dp | +11% primary | FAB, dialogs, search, date/time pickers | +| 4 | 8dp | +12% primary | (hover/focus increase only) | +| 5 | 12dp | +14% primary | (hover/focus increase only) | + +Elevation in MD3 is communicated through **tonal surface color**, not shadows. Shadows are only used when needed for additional protection against busy backgrounds. + +### Motion +MD3 Expressive (May 2025) introduced **spring-based motion physics** for components. The legacy easing/duration system is still used for **transitions** (enter/exit/shared-axis): + +| Easing | Duration | Transition type | +|--------|----------|-----------------| +| Emphasized | 500ms | Begin and end on screen | +| Emphasized decelerate | 400ms | Enter the screen | +| Emphasized accelerate | 200ms | Exit the screen | +| Standard | 300ms | Begin and end on screen (utility) | +| Standard decelerate | 250ms | Enter screen (utility) | +| Standard accelerate | 200ms | Exit screen (utility) | + +CSS easing values: +- Emphasized: `cubic-bezier(0.2, 0, 0, 1)` +- Emphasized decelerate: `cubic-bezier(0.05, 0.7, 0.1, 1)` +- Emphasized accelerate: `cubic-bezier(0.3, 0, 0.8, 0.15)` +- Standard: `cubic-bezier(0.2, 0, 0, 1)` +- Standard decelerate: `cubic-bezier(0, 0, 0, 1)` +- Standard accelerate: `cubic-bezier(0.3, 0, 1, 1)` + +## Component Quick Reference + +| Component | Web Element | Key Variants | Category | +|-----------|------------|--------------|----------| +| Button | `md-filled-button`, `md-outlined-button`, `md-text-button`, `md-elevated-button`, `md-filled-tonal-button` | Filled, Outlined, Text, Elevated, Tonal; 5 sizes (XS–XL); toggle | Actions | +| Button group | `md-button-group` | Standard, connected | Actions | +| Extended FAB | `md-extended-fab` | Surface, Primary, Secondary, Tertiary | Actions | +| FAB | `md-fab` | Small, Medium, Large | Actions | +| FAB menu | — | — | Actions | +| Icon button | `md-icon-button`, `md-filled-icon-button`, `md-filled-tonal-icon-button`, `md-outlined-icon-button` | Standard, Filled, Filled Tonal, Outlined | Actions | +| Segmented button | — | Single-select, Multi-select | Actions | +| Split button | — | — | Actions | +| Badge | — | Small (dot), Large (count) | Communication | +| Loading indicator | — | Linear, Circular | Communication | +| Progress indicator | `md-linear-progress`, `md-circular-progress` | Linear, Circular; determinate/indeterminate | Communication | +| Snackbar | — | Single-line, Two-line, Action | Communication | +| Tooltip | — | Plain, Rich | Communication | +| Card | — | Filled, Outlined, Elevated | Containment | +| Carousel | — | Multi-browse, Uncontained, Hero | Containment | +| Dialog | `md-dialog` | Basic, Full-screen | Containment | +| Bottom sheet | — | Standard, Modal | Sheets | +| Side sheet | — | Standard, Modal | Sheets | +| Divider | `md-divider` | Full-width, Inset | Containment | +| Checkbox | `md-checkbox` | — | Input | +| Chips | `md-chip-set`, `md-assist-chip`, `md-filter-chip`, `md-input-chip`, `md-suggestion-chip` | Assist, Filter, Input, Suggestion | Input | +| Date picker | — | Docked, Modal, Range | Input | +| Menu | `md-menu`, `md-menu-item` | — | Input | +| Radio button | `md-radio` | — | Input | +| Slider | `md-slider` | Continuous, Discrete, Range | Input | +| Switch | `md-switch` | With/without icon | Input | +| Text field | `md-filled-text-field`, `md-outlined-text-field` | Filled, Outlined | Input | +| Time picker | — | Docked, Modal | Input | +| App bar (top) | — | Center-aligned, Small, Medium, Large | Navigation | +| Navigation bar | `md-navigation-bar` | — | Navigation | +| Navigation drawer | `md-navigation-drawer` | Standard, Modal | Navigation | +| Navigation rail | — | — | Navigation | +| Search | — | Search bar, Search view | Navigation | +| Tabs | `md-tabs`, `md-primary-tab`, `md-secondary-tab` | Primary, Secondary | Navigation | +| Toolbar | — | — | Navigation | +| List | `md-list`, `md-list-item` | One-line, Two-line, Three-line | Data Display | + +**Note:** Components marked with `—` for web element don't have @material/web implementations yet. Use CSS custom properties with standard HTML for these. **Compose** mappings and examples live in `references/component-catalog.md`. + +Full component details with code examples: `references/component-catalog.md` + +## Jetpack Compose (primary) + +Use **`androidx.compose.material3`** with `MaterialTheme` and Material 3 composables (`Scaffold`, `Button`, `NavigationBar`, top app bars, etc.). + +- **Theming**: `MaterialTheme(colorScheme = …, typography = …, shapes = …)`. Prefer `dynamicLightColorScheme` / `dynamicDarkColorScheme` on **Android 12+ (API 31+)** when dynamic color is desired; otherwise `lightColorScheme` / `darkColorScheme` or generated theme code from Material Theme Builder. +- **Adaptive UI**: Window size classes, list-detail and supporting-pane layouts, foldables — see `references/layout-and-responsive.md` and `references/navigation-patterns.md`. +- **Edge-to-edge & insets**: Lay out content with `WindowInsets` / scaffold padding so bars and IME behave correctly — see `references/layout-and-responsive.md`. +- **Experimental APIs**: Some Material 3 APIs require `@OptIn(ExperimentalMaterial3Api::class)` or expressive opt-ins; match your BOM and compiler. + +```kotlin +MaterialTheme( + colorScheme = colorScheme, // from dynamicLightColorScheme / lightColorScheme / etc. + typography = Typography(), + shapes = Shapes(), +) { + // M3 content — prefer references for Scaffold, navigation, text fields +} +``` + +## Web (limited): @material/web + +**Important:** Per [Material Design 3 for Web](https://m3.material.io/develop/web), **Material Web Components are in maintenance mode** and **M3 Expressive is not implemented on Web**. Use `@material/web` for token-backed web UIs when appropriate, but do not treat it as equivalent to Compose for current Expressive features. + +### Setup + +```bash +npm install @material/web +``` + +### Import Components Individually + +Always import only the components you use — importing the entire package bloats the bundle: + +```javascript +// Good — individual imports +import '@material/web/button/filled-button.js'; +import '@material/web/button/outlined-button.js'; +import '@material/web/textfield/outlined-text-field.js'; +import '@material/web/icon/icon.js'; + +// Bad — never do this +import '@material/web'; // imports everything +``` + +### Basic Usage + +```html + + + + + + + + + + Get started + + + + + +``` + +### Theming with CSS Custom Properties + +Apply a custom theme by setting CSS custom properties on `:root` or any ancestor: + +```css +:root { + /* Color scheme (generate with @material/material-color-utilities) */ + --md-sys-color-primary: #6750A4; + --md-sys-color-on-primary: #FFFFFF; + --md-sys-color-primary-container: #EADDFF; + --md-sys-color-on-primary-container: #21005D; + --md-sys-color-secondary: #625B71; + --md-sys-color-on-secondary: #FFFFFF; + --md-sys-color-secondary-container: #E8DEF8; + --md-sys-color-on-secondary-container: #1D192B; + --md-sys-color-surface: #FEF7FF; + --md-sys-color-on-surface: #1D1B20; + --md-sys-color-surface-container: #F3EDF7; + --md-sys-color-outline: #79747E; + --md-sys-color-outline-variant: #CAC4D0; + + /* Typography */ + --md-sys-typescale-body-large-font: 'Roboto Flex', sans-serif; + --md-sys-typescale-body-large-size: 1rem; + --md-sys-typescale-body-large-weight: 400; + --md-sys-typescale-body-large-line-height: 1.5rem; + + /* Shape */ + --md-sys-shape-corner-full: 9999px; + --md-sys-shape-corner-medium: 12px; +} +``` + +### Component-Level Overrides + +Override individual component tokens for specific customization: + +```css +md-filled-button { + --md-filled-button-container-color: var(--md-sys-color-primary); + --md-filled-button-label-text-color: var(--md-sys-color-on-primary); + --md-filled-button-container-shape: var(--md-sys-shape-corner-full); + --md-filled-button-container-height: 40px; +} + +md-outlined-text-field { + --md-outlined-text-field-container-shape: var(--md-sys-shape-corner-small); + --md-outlined-text-field-focus-outline-color: var(--md-sys-color-primary); +} +``` + +### Dark Theme + +Apply dark theme by overriding color tokens on a class or media query: + +```css +@media (prefers-color-scheme: dark) { + :root { + --md-sys-color-primary: #D0BCFF; + --md-sys-color-on-primary: #381E72; + --md-sys-color-primary-container: #4F378B; + --md-sys-color-on-primary-container: #EADDFF; + --md-sys-color-surface: #141218; + --md-sys-color-on-surface: #E6E0E9; + --md-sys-color-surface-container: #211F26; + --md-sys-color-outline: #938F99; + --md-sys-color-outline-variant: #49454F; + } +} +``` + +Full theming guide: `references/theming-and-dynamic-color.md` + +## Common Patterns + +### App Shell + +Standard MD3 app with responsive navigation + top app bar + content area: + +```html +
+ +
+
+

+ Page Title +

+
+
+ +
+
+
+``` + +```css +.md3-app { + display: flex; + min-height: 100vh; + background: var(--md-sys-color-surface); + color: var(--md-sys-color-on-surface); +} + +.md3-nav-rail { + width: 80px; + background: var(--md-sys-color-surface); + border-right: 1px solid var(--md-sys-color-outline-variant); + display: flex; + flex-direction: column; + align-items: center; + padding-top: 12px; + gap: 12px; +} + +.md3-content { + flex: 1; + display: flex; + flex-direction: column; +} + +.md3-top-app-bar { + height: 64px; + padding: 0 16px; + display: flex; + align-items: center; + background: var(--md-sys-color-surface); +} + +.md3-body { + padding: 24px; + flex: 1; +} + +/* Responsive: switch to bottom nav on compact */ +@media (max-width: 599px) { + .md3-app { flex-direction: column; } + .md3-nav-rail { + order: 1; + width: 100%; + flex-direction: row; + justify-content: center; + border-right: none; + border-top: 1px solid var(--md-sys-color-outline-variant); + padding: 0; + } +} +``` + +### Card Grid + +```html +
+
+ Description +
+

Card Title

+

+ Supporting text for this card. +

+
+
+ Learn more + Action +
+
+
+``` + +```css +.md3-card-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 16px; +} + +.md3-card--outlined { + border: 1px solid var(--md-sys-color-outline-variant); + border-radius: var(--md-sys-shape-corner-medium, 12px); + background: var(--md-sys-color-surface); + overflow: hidden; +} + +.md3-card__content { padding: 16px; } +.md3-card__actions { padding: 8px 16px 16px; display: flex; gap: 8px; justify-content: flex-end; } +.md3-card__media { width: 100%; aspect-ratio: 16/9; object-fit: cover; } +``` + +### Form Layout + +```html +
+ + + +
+ Cancel + Submit +
+
+``` + +```css +.md3-form { + display: flex; + flex-direction: column; + gap: 16px; + max-width: 560px; +} + +.md3-form__actions { + display: flex; + gap: 8px; + justify-content: flex-end; + margin-top: 8px; +} +``` + +More patterns: `references/navigation-patterns.md`, `references/layout-and-responsive.md` + +## Anti-Patterns + +**Never do these when implementing MD3:** + +- **Mix MD2 and MD3 libraries**: Don't use `@material/mdc-*` (MD2) alongside `@material/web` (MD3). They have incompatible APIs and styling. +- **Hardcode colors**: Always use `var(--md-sys-color-*)` tokens, never raw hex/rgb values. Hardcoded colors break dynamic theming, dark mode, and contrast adjustment. +- **Ignore tonal pairing**: Only combine colors in their intended pairs (e.g., `primary` + `on-primary`, `surface-container` + `on-surface`). Arbitrary pairings break contrast in dynamic color and high contrast modes. +- **Use `outline` for dividers**: Use `outline-variant` for dividers. `outline` is for important boundaries like text field borders. +- **Import all of @material/web**: Always import individual component modules. Barrel imports include every component and destroy bundle size. +- **Use `border-radius` directly**: Use shape tokens (`var(--md-sys-shape-corner-medium)`) so shapes stay consistent with theming. +- **Use shadows for elevation by default**: MD3 communicates elevation through tonal surface color, not shadows. Only add shadows when elements need extra separation from busy backgrounds. +- **Apply frontend-design "avoid Roboto" rule**: On **Android**, **Roboto** is the default Material typeface; **web** often uses Roboto or Roboto Flex with MD3 tokens. Replace only when intentionally customizing the type scale. +- **Assume SSR compatibility**: `@material/web` uses Web Components (custom elements) which require JavaScript to render. They won't produce meaningful HTML in SSR without additional hydration strategies. +- **Ignore foldables and large screens**: MD3 is designed for all screen sizes. Don't ship phone-only layouts — use canonical layouts, multi-pane at 600dp+, and test on foldable/tablet emulators. Place no interactive content across the fold/hinge. +- **Stretch content to fill wide screens**: On Large (1200dp+) and Extra-large (1600dp+) windows, constrain content to a max width (840–1040dp). Endless-width text lines are unreadable. + +## Platform Notes + +### Flutter +```dart +MaterialApp( + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + ), +); +``` + +### Jetpack Compose +See **[Jetpack Compose (primary)](#jetpack-compose-primary)** above. Use `LocalContext.current` with `dynamicLightColorScheme` / `dynamicDarkColorScheme` only when `Build.VERSION.SDK_INT >= Build.VERSION_CODES.S` and dynamic color is enabled; otherwise supply static light/dark schemes. + +### Component Name Mapping +| Concept | Web | Flutter | Compose | +|---------|-----|---------|---------| +| Filled button | `md-filled-button` | `FilledButton` | `Button` | +| Outlined text field | `md-outlined-text-field` | `OutlinedTextField` | `OutlinedTextField` | +| FAB | `md-fab` | `FloatingActionButton` | `FloatingActionButton` | +| Navigation bar | `md-navigation-bar` | `NavigationBar` | `NavigationBar` | +| Switch | `md-switch` | `Switch` | `Switch` | + +## M3 Expressive (May 2025) + +The Expressive update adds visual richness while maintaining usability. **Availability differs by platform** — do not assume one stack implements everything. + +| Capability | Jetpack Compose | Flutter | Web (`@material/web`) | +|------------|-----------------|---------|------------------------| +| Expressive layout scaffold / adaptive layout | Compose-first via Material3 adaptive APIs and window size classes | Use Flutter adaptive/layout primitives | CSS/container queries/manual layout; no Material Web parity | +| 8dp spacing system | Use design tokens / `Dp` spacing constants; keep margins, padding, and gaps adaptive | Use theme spacing constants | CSS custom properties / design tokens | +| Expressive lists, menus, search, search app bar | Primary target per current Material guidance; check BOM and opt-ins | Check current Flutter Material docs | Spec-aligned custom implementation; `@material/web` is maintenance-only | +| Spring / motion physics | Supported in Material 3 (see `MotionScheme`, expressive APIs per BOM) | Varies by Flutter Material version | **Not** in Material Web; use easing/duration or custom motion | +| Emphasized typography | Via theme / type scale | Via theme | Token/CSS only; no full Expressive component set | +| Shape morphing | Compose-first in Google’s expressive rollout | Check current Flutter docs | **Not** in `@material/web` | +| New button sizes (XS–XL), toggle | Follow Compose Material3 components | Follow Flutter MD3 | Height/CSS approximations only | +| Extra corner tokens (e.g. large-increased) | `MaterialTheme.shapes` / tokens | Theme shapes | CSS `--md-sys-shape-*` | +| 3 contrast levels | Scheme builders / system | Plugins / manual | `SchemeContent` contrast parameter in JS utilities | +| Watches / XR form factors | Use Compose/Wear/XR-specific guidance where available | Platform-specific | Web/spatial UI custom implementation | + +**Web:** [Material Web is maintenance-only; M3 Expressive is not on Web](https://m3.material.io/develop/web). Use CSS easing/duration tokens as fallback for motion, not spring parity. + +**Legacy easing/duration** remains valid for **transitions** (enter/exit/shared-axis) where the spec still references them; see the Motion table below. + +## MD3 Compliance Audit + +When invoked with `audit` as the argument (e.g., `/material-3 audit`), or when asked to audit/review MD3 compliance, analyze the target app or page and produce a compliance report. + +### Audit Procedure + +1. **Identify the target**: The user provides a URL (use browser tools to inspect), file paths (read source), or a running app. +2. **Inspect the following categories** and score each 0–10: + +| Category | What to check | +|----------|--------------| +| **Color tokens** | **Web:** `--md-sys-color-*` / generated CSS. **Compose:** `MaterialTheme.colorScheme` roles (no arbitrary `Color(...)` for surfaces without reason). Proper tonal pairing (`onX` on `X`). Dark theme. **Flutter:** `ColorScheme` roles. | +| **Typography** | MD3 type scale: **Compose** `MaterialTheme.typography`; **web** typescale tokens; correct roles (Display, Headline, Title, Body, Label). | +| **Shape** | **Compose** `MaterialTheme.shapes` / component `Shape`; **web** `var(--md-sys-shape-*)`. Buttons: full; cards: medium; avoid magic numbers. | +| **Elevation** | Tonal elevation (`Surface` tonal/shadow as appropriate). **Web:** hover/focus where relevant. | +| **Components** | **Compose:** Material3 composables (`Button`, `Scaffold`, etc.). **Web:** `@material/web` or spec-aligned HTML/CSS. Correct variants. | +| **Layout** | Canonical layouts; **Compose** window size class / adaptive APIs; readable max width on large widths; foldable hinge avoidance. | +| **Navigation** | Bar / rail / drawer / drawers+**Compose** `NavHost` patterns per size class; predictive back where applicable. | +| **Motion** | **Compose** `MotionScheme` / expressive APIs when used; transitions may still use easing/duration. **Web:** CSS motion tokens fallback. | +| **Accessibility** | MD3 roles help, but **verify contrast**: UI components often need **3:1** for large text/borders and **4.5:1** for normal text (WCAG 2.x). TalkBack/semantics (Compose), focus order, touch targets (~48dp). **Web:** ARIA, keyboard. | +| **Theming** | **Compose:** `MaterialTheme` + light/dark/dynamic as designed. **Web:** CSS custom properties on `:root` or subtree. **Flutter:** `ThemeData` + `ColorScheme`. | + +3. **Generate the report**: + +``` +# MD3 Compliance Audit Report + +Target: [URL or file path] +Date: [date] +Overall Score: [X/100] + +## Scores by Category +| Category | Score | Status | +|----------------|-------|--------| +| Color tokens | X/10 | [pass/warn/fail] | +| Typography | X/10 | [pass/warn/fail] | +| Shape | X/10 | [pass/warn/fail] | +| Elevation | X/10 | [pass/warn/fail] | +| Components | X/10 | [pass/warn/fail] | +| Layout | X/10 | [pass/warn/fail] | +| Navigation | X/10 | [pass/warn/fail] | +| Motion | X/10 | [pass/warn/fail] | +| Accessibility | X/10 | [pass/warn/fail] | +| Theming | X/10 | [pass/warn/fail] | + +## Critical Issues +[List items scoring 0-3 with specific file:line references and fixes] + +## Warnings +[List items scoring 4-6 with recommendations] + +## Passing +[List items scoring 7-10 with notes on what's done well] + +## Recommended Fixes (Priority Order) +1. [Most impactful fix first] +2. ... +``` + +### Audit Methods + +**For a live URL** (browser or devtools): +- Inspect computed styles and CSS variables (`--md-sys-*`) +- Resize viewport or use responsive mode for breakpoints +- Capture screenshots at key widths if helpful + +**For source code** (file paths provided): +- **Compose/Kotlin:** `.kt` files — `MaterialTheme`, composables, `Color(0x…)` abuse, hard-coded `Dp`, missing `Modifier.semantics` where needed +- **Flutter:** `.dart` — `ThemeData`, `ColorScheme` +- **Web:** HTML/JSX/Vue/Svelte; CSS/SCSS for tokens +- Check **web** imports for `@material/web` vs `@material/mdc-*` (MD2) + +**Quick checks** (adapt paths to your stack): +``` +# Web: hardcoded colors +grep -rn '#[0-9a-fA-F]\{3,8\}' --include='*.css' --include='*.scss' + +# Compose: raw Color(...) audits (sample — tune for your codebase) +grep -rn 'Color(0x' --include='*.kt' + +# MD2 on web +grep -rn '@material/mdc-' --include='*.js' --include='*.ts' +``` + +**Browser automation** (if your environment exposes MCP browser tools): navigate, snapshot DOM/CSS variables, resize for breakpoints — optional, not required. + +### Scoring Guide + +- **9-10**: Fully MD3 compliant, uses correct tokens and patterns +- **7-8**: Mostly compliant, minor issues (e.g., a few hardcoded values) +- **4-6**: Partially compliant, some MD3 patterns but significant gaps +- **1-3**: Major violations, mostly non-MD3 or MD2 patterns +- **0**: Not applicable or completely absent + +Status thresholds: **pass** (7+), **warn** (4-6), **fail** (0-3) + +## Reference Documents + +- `references/color-system.md` — Color roles, tonal palettes, dynamic color, Compose + CSS mapping +- `references/typography-and-shape.md` — Type scale, shape corners, elevation, motion, Expressive notes +- `references/component-catalog.md` — Components: Compose + `@material/web` where applicable +- `references/navigation-patterns.md` — Navigation selection, Compose-first adaptive patterns +- `references/layout-and-responsive.md` — Breakpoints, canonical layouts, insets, foldables +- `references/theming-and-dynamic-color.md` — Theming: Compose first, then Flutter and web diff --git a/.agents/skills/material-3/references/color-system.md b/.agents/skills/material-3/references/color-system.md new file mode 100644 index 0000000..61557d8 --- /dev/null +++ b/.agents/skills/material-3/references/color-system.md @@ -0,0 +1,358 @@ +# MD3 Color System + +Complete reference for Material Design 3's color system: roles, tonal palettes, dynamic color, and scheme generation. + +## Color Roles + +MD3 defines 29+ color roles organized into groups. **Jetpack Compose:** map to `MaterialTheme.colorScheme` (e.g. `primary`, `onPrimary`). **Web:** each role exists as a CSS custom property `--md-sys-color-{role-name}`. + +### Accent Colors + +Three accent groups (primary, secondary, tertiary) each have 4 roles: + +| Role | CSS Token | Purpose | +|------|-----------|---------| +| Primary | `--md-sys-color-primary` | High-emphasis fills, text, icons against surface | +| On Primary | `--md-sys-color-on-primary` | Text and icons on primary | +| Primary Container | `--md-sys-color-primary-container` | Standout fill for key components (FAB, etc.) | +| On Primary Container | `--md-sys-color-on-primary-container` | Text and icons on primary-container | +| Secondary | `--md-sys-color-secondary` | Less prominent fills, text, icons | +| On Secondary | `--md-sys-color-on-secondary` | Text and icons on secondary | +| Secondary Container | `--md-sys-color-secondary-container` | Recessive components (tonal buttons) | +| On Secondary Container | `--md-sys-color-on-secondary-container` | Text and icons on secondary-container | +| Tertiary | `--md-sys-color-tertiary` | Complementary fills, text, icons | +| On Tertiary | `--md-sys-color-on-tertiary` | Text and icons on tertiary | +| Tertiary Container | `--md-sys-color-tertiary-container` | Complementary container fill | +| On Tertiary Container | `--md-sys-color-on-tertiary-container` | Text and icons on tertiary-container | + +**Usage guidance:** +- **Primary**: Most prominent components — FABs, high-emphasis buttons, active states +- **Secondary**: Less prominent components — filter chips, tonal buttons, selection states +- **Tertiary**: Contrasting accents that balance primary/secondary — input fields, badges + +### Error Colors + +Static colors that don't change with dynamic color schemes: + +| Role | CSS Token | Purpose | +|------|-----------|---------| +| Error | `--md-sys-color-error` | Attention-grabbing color for urgent elements | +| On Error | `--md-sys-color-on-error` | Text and icons on error | +| Error Container | `--md-sys-color-error-container` | Error container fill | +| On Error Container | `--md-sys-color-on-error-container` | Text and icons on error-container | + +### Surface Colors + +| Role | CSS Token | Purpose | +|------|-----------|---------| +| Surface | `--md-sys-color-surface` | Default background color | +| On Surface | `--md-sys-color-on-surface` | Text and icons on any surface | +| On Surface Variant | `--md-sys-color-on-surface-variant` | Lower-emphasis text/icons on surface | +| Surface Container Lowest | `--md-sys-color-surface-container-lowest` | Lowest-emphasis container | +| Surface Container Low | `--md-sys-color-surface-container-low` | Low-emphasis container | +| Surface Container | `--md-sys-color-surface-container` | Default container (navigation areas) | +| Surface Container High | `--md-sys-color-surface-container-high` | High-emphasis container | +| Surface Container Highest | `--md-sys-color-surface-container-highest` | Highest-emphasis container | +| Surface Dim | `--md-sys-color-surface-dim` | Dimmest surface in both themes | +| Surface Bright | `--md-sys-color-surface-bright` | Brightest surface in both themes | + +**Surface container hierarchy**: Use `surface` for background, `surface-container` for navigation. The 5 container levels create visual hierarchy and nesting depth, especially useful for expanded layouts with multiple panes. + +**Surface dim/bright**: Unlike regular surface (which flips from light to dark), dim and bright maintain their relative brightness in both themes. Use bright for areas that should always be the lightest, dim for areas that should always be the dimmest. + +### Inverse Colors + +For elements that contrast against the surrounding UI (e.g., snackbars): + +| Role | CSS Token | Purpose | +|------|-----------|---------| +| Inverse Surface | `--md-sys-color-inverse-surface` | Background for contrasting elements | +| Inverse On Surface | `--md-sys-color-inverse-on-surface` | Text on inverse-surface | +| Inverse Primary | `--md-sys-color-inverse-primary` | Actionable text on inverse-surface | + +### Outline Colors + +| Role | CSS Token | Purpose | +|------|-----------|---------| +| Outline | `--md-sys-color-outline` | Important boundaries (text field borders, 3:1 contrast) | +| Outline Variant | `--md-sys-color-outline-variant` | Decorative elements (dividers, card borders) | + +**Important**: Don't use `outline` for dividers — use `outline-variant`. Don't use `outline-variant` for interactive boundaries that need 3:1 contrast — use `outline`. + +### Fixed Accent Colors (Add-on) + +These maintain the same color in both light and dark themes (unlike regular container colors which change tone): + +| Role | CSS Token | +|------|-----------| +| Primary Fixed | `--md-sys-color-primary-fixed` | +| Primary Fixed Dim | `--md-sys-color-primary-fixed-dim` | +| On Primary Fixed | `--md-sys-color-on-primary-fixed` | +| On Primary Fixed Variant | `--md-sys-color-on-primary-fixed-variant` | +| Secondary Fixed | `--md-sys-color-secondary-fixed` | +| Secondary Fixed Dim | `--md-sys-color-secondary-fixed-dim` | +| On Secondary Fixed | `--md-sys-color-on-secondary-fixed` | +| On Secondary Fixed Variant | `--md-sys-color-on-secondary-fixed-variant` | +| Tertiary Fixed | `--md-sys-color-tertiary-fixed` | +| Tertiary Fixed Dim | `--md-sys-color-tertiary-fixed-dim` | +| On Tertiary Fixed | `--md-sys-color-on-tertiary-fixed` | +| On Tertiary Fixed Variant | `--md-sys-color-on-tertiary-fixed-variant` | + +**Caution**: Fixed colors don't adapt to theme, so they may cause contrast issues. Use regular accent roles for elements where contrast is critical. + +## Tonal Palette System + +MD3 generates colors from a **seed color** through the tonal palette system: + +### How It Works + +1. A **seed color** (hex value) is chosen +2. The seed generates **5 tonal palettes**: Primary, Secondary, Tertiary, Neutral, Neutral-Variant +3. Each palette uses tonal stops along **0–100** (commonly 16 key stops: 0, 10, 20, 25, 30, 35, 40, 50, 60, 70, 80, 90, 95, 98, 99, 100) +4. Color roles are mapped to specific tonal values depending on light or dark scheme + +### Tonal Value Mapping (Light Scheme) + +| Role | Tonal Palette | Tone | +|------|--------------|------| +| Primary | Primary | 40 | +| On Primary | Primary | 100 | +| Primary Container | Primary | 90 | +| On Primary Container | Primary | 10 | +| Surface | Neutral | 98 | +| On Surface | Neutral | 10 | +| Surface Container | Neutral | 94 | +| Surface Container Low | Neutral | 96 | +| Surface Container Lowest | Neutral | 100 | +| Surface Container High | Neutral | 92 | +| Surface Container Highest | Neutral | 90 | +| Outline | Neutral-Variant | 50 | +| Outline Variant | Neutral-Variant | 80 | + +### Tonal Value Mapping (Dark Scheme) + +| Role | Tonal Palette | Tone | +|------|--------------|------| +| Primary | Primary | 80 | +| On Primary | Primary | 20 | +| Primary Container | Primary | 30 | +| On Primary Container | Primary | 90 | +| Surface | Neutral | 6 | +| On Surface | Neutral | 90 | +| Surface Container | Neutral | 12 | +| Surface Container Low | Neutral | 10 | +| Surface Container Lowest | Neutral | 4 | +| Surface Container High | Neutral | 17 | +| Surface Container Highest | Neutral | 22 | +| Outline | Neutral-Variant | 60 | +| Outline Variant | Neutral-Variant | 30 | + +## Color Pairing Rules + +Colors must only be used in their intended pairs to ensure accessible contrast: + +| Container/Fill | Text/Icon Color | +|---------------|----------------| +| `primary` | `on-primary` | +| `primary-container` | `on-primary-container` | +| `secondary` | `on-secondary` | +| `secondary-container` | `on-secondary-container` | +| `tertiary` | `on-tertiary` | +| `tertiary-container` | `on-tertiary-container` | +| `error` | `on-error` | +| `error-container` | `on-error-container` | +| `surface` | `on-surface` or `on-surface-variant` | +| `surface-container-*` | `on-surface` or `on-surface-variant` | +| `inverse-surface` | `inverse-on-surface` or `inverse-primary` | + +**Never pair colors outside their intended pairs** — this breaks contrast guarantees, especially under dynamic color and high contrast modes. + +## Dynamic Color + +Dynamic color creates personalized color schemes from external sources: + +### User-Generated (Wallpaper) +The OS extracts a seed color from the user's wallpaper and generates a scheme. **Android:** `dynamicLightColorScheme` / `dynamicDarkColorScheme` on **Android 12+ (API 31+)**. **Web:** there is **no** browser wallpaper dynamic-color API equivalent; you can derive a seed from **content** (e.g. images) with libraries, but that is app-specific, not system wallpaper theming. + +### Content-Based +A seed color is extracted from in-app content (album art, book cover, etc.) to create a contextual scheme. + +### Generating a Scheme with JavaScript + +```javascript +import { + argbFromHex, + themeFromSourceColor, + applyTheme, +} from '@material/material-color-utilities'; + +// Generate a theme from a seed color +const theme = themeFromSourceColor(argbFromHex('#6750A4')); + +// Apply to the document +applyTheme(theme, { target: document.body, dark: false }); +``` + +### Manual CSS Generation from Seed + +```javascript +import { + argbFromHex, + hexFromArgb, + SchemeContent, + Hct, +} from '@material/material-color-utilities'; + +function generateScheme(seedHex, isDark = false) { + const hct = Hct.fromInt(argbFromHex(seedHex)); + const scheme = new SchemeContent(hct, isDark, 0.0); // 0.0 = standard contrast + + return { + '--md-sys-color-primary': hexFromArgb(scheme.primary), + '--md-sys-color-on-primary': hexFromArgb(scheme.onPrimary), + '--md-sys-color-primary-container': hexFromArgb(scheme.primaryContainer), + '--md-sys-color-on-primary-container': hexFromArgb(scheme.onPrimaryContainer), + '--md-sys-color-secondary': hexFromArgb(scheme.secondary), + '--md-sys-color-on-secondary': hexFromArgb(scheme.onSecondary), + '--md-sys-color-secondary-container': hexFromArgb(scheme.secondaryContainer), + '--md-sys-color-on-secondary-container': hexFromArgb(scheme.onSecondaryContainer), + '--md-sys-color-tertiary': hexFromArgb(scheme.tertiary), + '--md-sys-color-on-tertiary': hexFromArgb(scheme.onTertiary), + '--md-sys-color-tertiary-container': hexFromArgb(scheme.tertiaryContainer), + '--md-sys-color-on-tertiary-container': hexFromArgb(scheme.onTertiaryContainer), + '--md-sys-color-error': hexFromArgb(scheme.error), + '--md-sys-color-on-error': hexFromArgb(scheme.onError), + '--md-sys-color-error-container': hexFromArgb(scheme.errorContainer), + '--md-sys-color-on-error-container': hexFromArgb(scheme.onErrorContainer), + '--md-sys-color-surface': hexFromArgb(scheme.surface), + '--md-sys-color-on-surface': hexFromArgb(scheme.onSurface), + '--md-sys-color-on-surface-variant': hexFromArgb(scheme.onSurfaceVariant), + '--md-sys-color-surface-container': hexFromArgb(scheme.surfaceContainer), + '--md-sys-color-surface-container-low': hexFromArgb(scheme.surfaceContainerLow), + '--md-sys-color-surface-container-lowest': hexFromArgb(scheme.surfaceContainerLowest), + '--md-sys-color-surface-container-high': hexFromArgb(scheme.surfaceContainerHigh), + '--md-sys-color-surface-container-highest': hexFromArgb(scheme.surfaceContainerHighest), + '--md-sys-color-outline': hexFromArgb(scheme.outline), + '--md-sys-color-outline-variant': hexFromArgb(scheme.outlineVariant), + '--md-sys-color-inverse-surface': hexFromArgb(scheme.inverseSurface), + '--md-sys-color-inverse-on-surface': hexFromArgb(scheme.inverseOnSurface), + '--md-sys-color-inverse-primary': hexFromArgb(scheme.inversePrimary), + }; +} + +// Apply to document +function applyScheme(seedHex, isDark = false) { + const tokens = generateScheme(seedHex, isDark); + const root = document.documentElement; + for (const [property, value] of Object.entries(tokens)) { + root.style.setProperty(property, value); + } +} +``` + +## Color Harmonization + +When integrating custom brand colors that don't come from the seed, use harmonization to blend them into the tonal system: + +```javascript +import { Blend } from '@material/material-color-utilities'; + +// Harmonize a custom color with the primary color +const harmonized = Blend.harmonize(customColorArgb, primaryColorArgb); +``` + +This shifts the custom color's hue slightly toward the scheme's primary, making it feel cohesive without losing its identity. + +## User-Controlled Contrast (May 2025) + +MD3 now supports 3 contrast levels: +- **Standard** (0.0): Default contrast +- **Medium** (0.5): Increased tonal distance between roles +- **High** (1.0): Maximum tonal distance for vision accessibility + +```javascript +// Generate high contrast scheme +const scheme = new SchemeContent(hct, isDark, 1.0); // 1.0 = high contrast +``` + +The contrast parameter adjusts the tonal distance between paired roles, increasing legibility without changing the overall color feel. + +## Baseline Color Scheme (Default Values) + +The static baseline scheme for products not using dynamic color: + +### Light Theme +```css +:root { + --md-sys-color-primary: #6750A4; + --md-sys-color-on-primary: #FFFFFF; + --md-sys-color-primary-container: #EADDFF; + --md-sys-color-on-primary-container: #21005D; + --md-sys-color-secondary: #625B71; + --md-sys-color-on-secondary: #FFFFFF; + --md-sys-color-secondary-container: #E8DEF8; + --md-sys-color-on-secondary-container: #1D192B; + --md-sys-color-tertiary: #7D5260; + --md-sys-color-on-tertiary: #FFFFFF; + --md-sys-color-tertiary-container: #FFD8E4; + --md-sys-color-on-tertiary-container: #31111D; + --md-sys-color-error: #B3261E; + --md-sys-color-on-error: #FFFFFF; + --md-sys-color-error-container: #F9DEDC; + --md-sys-color-on-error-container: #410E0B; + --md-sys-color-surface: #FEF7FF; + --md-sys-color-on-surface: #1D1B20; + --md-sys-color-on-surface-variant: #49454F; + --md-sys-color-surface-container-lowest: #FFFFFF; + --md-sys-color-surface-container-low: #F7F2FA; + --md-sys-color-surface-container: #F3EDF7; + --md-sys-color-surface-container-high: #ECE6F0; + --md-sys-color-surface-container-highest: #E6E0E9; + --md-sys-color-surface-dim: #DED8E1; + --md-sys-color-surface-bright: #FEF7FF; + --md-sys-color-outline: #79747E; + --md-sys-color-outline-variant: #CAC4D0; + --md-sys-color-inverse-surface: #322F35; + --md-sys-color-inverse-on-surface: #F5EFF7; + --md-sys-color-inverse-primary: #D0BCFF; +} +``` + +### Dark Theme +```css +@media (prefers-color-scheme: dark) { + :root { + --md-sys-color-primary: #D0BCFF; + --md-sys-color-on-primary: #381E72; + --md-sys-color-primary-container: #4F378B; + --md-sys-color-on-primary-container: #EADDFF; + --md-sys-color-secondary: #CCC2DC; + --md-sys-color-on-secondary: #332D41; + --md-sys-color-secondary-container: #4A4458; + --md-sys-color-on-secondary-container: #E8DEF8; + --md-sys-color-tertiary: #EFB8C8; + --md-sys-color-on-tertiary: #492532; + --md-sys-color-tertiary-container: #633B48; + --md-sys-color-on-tertiary-container: #FFD8E4; + --md-sys-color-error: #F2B8B5; + --md-sys-color-on-error: #601410; + --md-sys-color-error-container: #8C1D18; + --md-sys-color-on-error-container: #F9DEDC; + --md-sys-color-surface: #141218; + --md-sys-color-on-surface: #E6E0E9; + --md-sys-color-on-surface-variant: #CAC4D0; + --md-sys-color-surface-container-lowest: #0F0D13; + --md-sys-color-surface-container-low: #1D1B20; + --md-sys-color-surface-container: #211F26; + --md-sys-color-surface-container-high: #2B2930; + --md-sys-color-surface-container-highest: #36343B; + --md-sys-color-surface-dim: #141218; + --md-sys-color-surface-bright: #3B383E; + --md-sys-color-outline: #938F99; + --md-sys-color-outline-variant: #49454F; + --md-sys-color-inverse-surface: #E6E0E9; + --md-sys-color-inverse-on-surface: #322F35; + --md-sys-color-inverse-primary: #6750A4; + } +} +``` diff --git a/.agents/skills/material-3/references/component-catalog.md b/.agents/skills/material-3/references/component-catalog.md new file mode 100644 index 0000000..dd99a73 --- /dev/null +++ b/.agents/skills/material-3/references/component-catalog.md @@ -0,0 +1,836 @@ +# MD3 Component Catalog + +Complete reference for Material Design 3 components. **Primary mappings:** Jetpack Compose (`androidx.compose.material3`) where most users ship UIs today; **web** uses `@material/web` element names and imports — [Material Web is maintenance-only](https://m3.material.io/develop/web). + +## Google I/O 2026 Component Updates + +Material's [I/O 2026 update](https://m3.material.io/blog/whats-new-at-io26) highlights refreshed expressive guidance for **lists**, **menus**, **search**, and **search app bars**, with Jetpack Compose as the primary implementation path. When implementing these in Android: + +- Prefer current `androidx.compose.material3` components and verify expressive APIs against the active Material3 BOM. +- Expect expressive variants to include richer visual style, motion, and flexible configuration. +- Treat web implementations as spec-aligned approximations unless Material Web exposes an equivalent component; Material Web remains maintenance-only. + +## Actions + +### Buttons + +MD3 has 5 button types ordered by emphasis: Filled > Filled Tonal > Elevated > Outlined > Text. + +**General attributes** (shared by all button types): + +| Attribute | Type | Description | +|-----------|------|-------------| +| `disabled` | boolean | Disables the button | +| `href` | string | Turns button into a link | +| `target` | string | Link target (`_blank`, etc.) | +| `trailing-icon` | boolean | Moves icon to trailing position | +| `type` | string | Form type: `button`, `submit`, `reset` | + +#### Filled Button +**Element**: `md-filled-button` | **Import**: `@material/web/button/filled-button.js` +**Use when**: Primary action, highest emphasis. + +```html +Get started + + arrow_forward + Sign up + +``` + +**Customize**: `--md-filled-button-container-color`, `--md-filled-button-label-text-color`, `--md-filled-button-container-shape`, `--md-filled-button-container-height` + +#### Filled Tonal Button +**Element**: `md-filled-tonal-button` | **Import**: `@material/web/button/filled-tonal-button.js` +**Use when**: Medium emphasis, softer than filled. Secondary actions alongside a filled button. + +```html +Save draft +``` + +**Customize**: `--md-filled-tonal-button-container-color`, `--md-filled-tonal-button-label-text-color` + +#### Elevated Button +**Element**: `md-elevated-button` | **Import**: `@material/web/button/elevated-button.js` +**Use when**: Medium emphasis with shadow. Use on colored backgrounds where tonal button blends in. + +```html +Add to cart +``` + +#### Outlined Button +**Element**: `md-outlined-button` | **Import**: `@material/web/button/outlined-button.js` +**Use when**: Medium emphasis, neutral. Good for secondary actions. + +```html +Cancel +``` + +**Customize**: `--md-outlined-button-outline-color`, `--md-outlined-button-outline-width` + +#### Text Button +**Element**: `md-text-button` | **Import**: `@material/web/button/text-button.js` +**Use when**: Lowest emphasis. Inline actions, dialog actions, less important options. + +```html +Learn more +``` + +#### Button Sizes (Expressive) +Buttons now support 5 sizes: extra-small, small (default), medium, large, extra-large. Set via CSS: +```css +md-filled-button { --md-filled-button-container-height: 32px; } /* XS */ +md-filled-button { --md-filled-button-container-height: 40px; } /* S (default) */ +md-filled-button { --md-filled-button-container-height: 48px; } /* M */ +md-filled-button { --md-filled-button-container-height: 56px; } /* L */ +md-filled-button { --md-filled-button-container-height: 64px; } /* XL */ +``` + +**A11y**: Buttons have built-in button role. Use `aria-label` when using icon-only buttons. Minimum touch target 48x48dp. + +### Button Group +**Element**: `md-button-group` | **Import**: `@material/web/button/button-group.js` +**Use when**: Grouping related actions together with connected visual treatment. + +```html + + Day + Week + Month + +``` + +### FAB (Floating Action Button) +**Element**: `md-fab` | **Import**: `@material/web/fab/fab.js` +**Use when**: The single most important action on a screen. + +| Attribute | Type | Description | +|-----------|------|-------------| +| `size` | string | `small`, `medium` (default), `large` | +| `variant` | string | `surface`, `primary`, `secondary`, `tertiary` | +| `label` | string | Text label (for extended FAB) | + +```html + + add + + + + edit + +``` + +**Customize**: `--md-fab-container-color`, `--md-fab-container-shape`, `--md-fab-icon-color` +**A11y**: Always provide `aria-label` since FABs are icon-only. + +### Extended FAB +**Element**: `md-extended-fab` | **Import**: `@material/web/fab/extended-fab.js` +**Use when**: Primary action with explanatory text. + +```html + + edit + +``` + +### Icon Button +**Element**: `md-icon-button` | **Import**: `@material/web/iconbutton/icon-button.js` + +4 variants with separate elements: + +| Variant | Element | Import | +|---------|---------|--------| +| Standard | `md-icon-button` | `@material/web/iconbutton/icon-button.js` | +| Filled | `md-filled-icon-button` | `@material/web/iconbutton/filled-icon-button.js` | +| Filled Tonal | `md-filled-tonal-icon-button` | `@material/web/iconbutton/filled-tonal-icon-button.js` | +| Outlined | `md-outlined-icon-button` | `@material/web/iconbutton/outlined-icon-button.js` | + +| Attribute | Type | Description | +|-----------|------|-------------| +| `toggle` | boolean | Enables toggle behavior | +| `selected` | boolean | Selected state (when toggle) | + +```html + + settings + + + + + favorite_border + favorite + +``` + +**A11y**: Always provide `aria-label`. Toggle buttons should have descriptive labels for both states. + +### Segmented Buttons +**No @material/web element yet.** Implement with standard HTML + MD3 tokens: + +```html +
+ + +
+``` + +```css +.md3-segmented-buttons { + display: flex; + border: 1px solid var(--md-sys-color-outline); + border-radius: var(--md-sys-shape-corner-full); + overflow: hidden; +} +.md3-segmented-button { + flex: 1; + padding: 10px 16px; + border: none; + background: transparent; + color: var(--md-sys-color-on-surface); + cursor: pointer; + font: var(--md-sys-typescale-label-large); + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} +.md3-segmented-button--selected { + background: var(--md-sys-color-secondary-container); + color: var(--md-sys-color-on-secondary-container); +} +``` + +## Communication + +### Badge +**No @material/web element yet.** Implement with CSS: + +```html +
+ notifications + 3 +
+``` + +```css +.md3-badge-container { position: relative; display: inline-flex; } +.md3-badge { + position: absolute; + top: -4px; + right: -4px; + background: var(--md-sys-color-error); + color: var(--md-sys-color-on-error); + border-radius: var(--md-sys-shape-corner-full); + font: var(--md-sys-typescale-label-small); +} +.md3-badge--small { width: 6px; height: 6px; padding: 0; } /* dot only */ +.md3-badge--large { min-width: 16px; height: 16px; padding: 0 4px; text-align: center; } +``` + +### Progress Indicator +**Elements**: `md-linear-progress`, `md-circular-progress` +**Import**: `@material/web/progress/linear-progress.js`, `@material/web/progress/circular-progress.js` + +| Attribute | Type | Description | +|-----------|------|-------------| +| `value` | number | Current progress (0–1) | +| `max` | number | Maximum value (default 1) | +| `indeterminate` | boolean | Shows indeterminate animation | +| `four-color` | boolean | Four-color indeterminate variant | + +```html + + + + + + + +``` + +**Customize**: `--md-linear-progress-active-indicator-color`, `--md-circular-progress-active-indicator-color` +**A11y**: Has built-in `progressbar` role. Add `aria-label` for context (e.g., "Loading messages"). + +### Snackbar +**No @material/web element yet.** Implement with standard HTML: + +```html +
+ Message sent + Undo + + close + +
+``` + +```css +.md3-snackbar { + background: var(--md-sys-color-inverse-surface); + color: var(--md-sys-color-inverse-on-surface); + border-radius: var(--md-sys-shape-corner-extra-small); + padding: 12px 16px; + display: flex; + align-items: center; + gap: 8px; + min-height: 48px; + max-width: 560px; +} +``` + +### Tooltip +**No @material/web element yet.** + +Two types: +- **Plain**: Short text label, appears on hover/focus. Use for icon buttons and truncated text. +- **Rich**: Multi-line with optional actions. Use for complex explanations. + +## Containment + +### Card +**No @material/web element yet.** Three variants: + +| Variant | Appearance | Elevation | +|---------|-----------|-----------| +| Filled | Surface-container-highest fill, no border | Level 0 | +| Outlined | Surface fill, outline-variant border | Level 0 | +| Elevated | Surface-container-low fill, shadow | Level 1 | + +```html + +
+
+

Title

+

+ Supporting text +

+
+
+ Action +
+
+ + +
...
+ + +
...
+``` + +```css +.md3-card { + border-radius: var(--md-sys-shape-corner-medium, 12px); + overflow: hidden; +} +.md3-card--outlined { + background: var(--md-sys-color-surface); + border: 1px solid var(--md-sys-color-outline-variant); +} +.md3-card--filled { + background: var(--md-sys-color-surface-container-highest); +} +.md3-card--elevated { + background: var(--md-sys-color-surface-container-low); + box-shadow: 0 1px 2px rgba(0,0,0,0.3), 0 1px 3px 1px rgba(0,0,0,0.15); +} +.md3-card__content { padding: 16px; } +.md3-card__actions { padding: 16px; display: flex; gap: 8px; justify-content: flex-end; } +``` + +### Dialog +**Element**: `md-dialog` | **Import**: `@material/web/dialog/dialog.js` + +| Attribute | Type | Description | +|-----------|------|-------------| +| `open` | boolean | Shows the dialog | +| `type` | string | `alert` (default) | + +```html + +
Confirm action
+
+ Are you sure you want to proceed? +
+
+ Cancel + Confirm +
+
+``` + +**A11y**: Dialog traps focus automatically. Use `slot="headline"` for accessible title. + +### Bottom Sheet +**No @material/web element yet.** Two variants: Standard (persistent, coexists with content) and Modal (blocks interaction, has scrim). + +### Side Sheet +**No @material/web element yet.** Two variants: Standard (docked alongside content) and Modal (overlays content with scrim). + +### Divider +**Element**: `md-divider` | **Import**: `@material/web/divider/divider.js` + +| Attribute | Type | Description | +|-----------|------|-------------| +| `inset` | boolean | Adds inset on both sides | +| `inset-start` | boolean | Adds inset on start side | +| `inset-end` | boolean | Adds inset on end side | + +```html + + +``` + +### Carousel +**No @material/web element yet.** Three configurations: +- **Multi-browse**: Multiple items visible, scrollable +- **Uncontained**: Items extend beyond viewport edges +- **Hero**: One large featured item with smaller previews + +## Input + +### Checkbox +**Element**: `md-checkbox` | **Import**: `@material/web/checkbox/checkbox.js` + +| Attribute | Type | Description | +|-----------|------|-------------| +| `checked` | boolean | Checked state | +| `indeterminate` | boolean | Indeterminate state | +| `disabled` | boolean | Disabled state | +| `required` | boolean | Required for form validation | + +```html + + + +``` + +**A11y**: Wrap in `