767 lines
20 KiB
Markdown
767 lines
20 KiB
Markdown
# 🎨 Руководство по Material Web Components 3
|
||
|
||
**Только Material компоненты! Никаких собственных компонентов!**
|
||
|
||
---
|
||
|
||
## 📦 Установка и настройка
|
||
|
||
```bash
|
||
npm install @material/web
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Настройка TypeScript
|
||
|
||
Создать файл `types/material-web.d.ts`:
|
||
|
||
```typescript
|
||
// Декларации для Material Web Components
|
||
declare namespace JSX {
|
||
interface IntrinsicElements {
|
||
// Buttons
|
||
'md-filled-button': any;
|
||
'md-outlined-button': any;
|
||
'md-text-button': any;
|
||
'md-elevated-button': any;
|
||
'md-tonal-button': any;
|
||
'md-filled-tonal-button': any;
|
||
|
||
// Text Fields
|
||
'md-filled-text-field': any;
|
||
'md-outlined-text-field': any;
|
||
|
||
// Cards
|
||
'md-filled-card': any;
|
||
'md-elevated-card': any;
|
||
'md-outlined-card': any;
|
||
|
||
// Lists
|
||
'md-list': any;
|
||
'md-list-item': any;
|
||
|
||
// Navigation
|
||
'md-navigation-bar': any;
|
||
'md-navigation-tab': any;
|
||
'md-navigation-drawer': any;
|
||
'md-navigation-drawer-modal': any;
|
||
|
||
// Dialogs & Sheets
|
||
'md-dialog': any;
|
||
|
||
// Chips
|
||
'md-chip-set': any;
|
||
'md-assist-chip': any;
|
||
'md-filter-chip': any;
|
||
'md-input-chip': any;
|
||
'md-suggestion-chip': any;
|
||
|
||
// Icons
|
||
'md-icon': any;
|
||
'md-icon-button': any;
|
||
'md-filled-icon-button': any;
|
||
'md-tonal-icon-button': any;
|
||
'md-outlined-icon-button': any;
|
||
|
||
// Form Controls
|
||
'md-checkbox': any;
|
||
'md-radio': any;
|
||
'md-switch': any;
|
||
'md-slider': any;
|
||
|
||
// Select
|
||
'md-filled-select': any;
|
||
'md-outlined-select': any;
|
||
'md-select-option': any;
|
||
|
||
// Menus
|
||
'md-menu': any;
|
||
'md-menu-item': any;
|
||
'md-sub-menu': any;
|
||
|
||
// Progress
|
||
'md-circular-progress': any;
|
||
'md-linear-progress': any;
|
||
|
||
// FAB
|
||
'md-fab': any;
|
||
'md-branded-fab': any;
|
||
|
||
// Badges
|
||
'md-badge': any;
|
||
|
||
// Divider
|
||
'md-divider': any;
|
||
|
||
// Tabs
|
||
'md-tabs': any;
|
||
'md-primary-tab': any;
|
||
'md-secondary-tab': any;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📚 Импорт компонентов
|
||
|
||
Создать файл `lib/material-components.ts`:
|
||
|
||
```typescript
|
||
// Buttons
|
||
import '@material/web/button/filled-button.js';
|
||
import '@material/web/button/outlined-button.js';
|
||
import '@material/web/button/text-button.js';
|
||
import '@material/web/button/elevated-button.js';
|
||
import '@material/web/button/tonal-button.js';
|
||
|
||
// Text Fields
|
||
import '@material/web/textfield/filled-text-field.js';
|
||
import '@material/web/textfield/outlined-text-field.js';
|
||
|
||
// Cards
|
||
import '@material/web/labs/card/filled-card.js';
|
||
import '@material/web/labs/card/elevated-card.js';
|
||
import '@material/web/labs/card/outlined-card.js';
|
||
|
||
// Lists
|
||
import '@material/web/list/list.js';
|
||
import '@material/web/list/list-item.js';
|
||
|
||
// Navigation (из labs)
|
||
import '@material/web/labs/navigationbar/navigation-bar.js';
|
||
import '@material/web/labs/navigationtab/navigation-tab.js';
|
||
import '@material/web/labs/navigationdrawer/navigation-drawer.js';
|
||
|
||
// Dialogs
|
||
import '@material/web/dialog/dialog.js';
|
||
|
||
// Chips
|
||
import '@material/web/chips/chip-set.js';
|
||
import '@material/web/chips/assist-chip.js';
|
||
import '@material/web/chips/filter-chip.js';
|
||
import '@material/web/chips/input-chip.js';
|
||
import '@material/web/chips/suggestion-chip.js';
|
||
|
||
// Icons
|
||
import '@material/web/icon/icon.js';
|
||
import '@material/web/iconbutton/icon-button.js';
|
||
import '@material/web/iconbutton/filled-icon-button.js';
|
||
import '@material/web/iconbutton/tonal-icon-button.js';
|
||
import '@material/web/iconbutton/outlined-icon-button.js';
|
||
|
||
// Form Controls
|
||
import '@material/web/checkbox/checkbox.js';
|
||
import '@material/web/radio/radio.js';
|
||
import '@material/web/switch/switch.js';
|
||
import '@material/web/slider/slider.js';
|
||
|
||
// Select
|
||
import '@material/web/select/filled-select.js';
|
||
import '@material/web/select/outlined-select.js';
|
||
import '@material/web/select/select-option.js';
|
||
|
||
// Menus
|
||
import '@material/web/menu/menu.js';
|
||
import '@material/web/menu/menu-item.js';
|
||
import '@material/web/menu/sub-menu.js';
|
||
|
||
// Progress
|
||
import '@material/web/progress/circular-progress.js';
|
||
import '@material/web/progress/linear-progress.js';
|
||
|
||
// FAB
|
||
import '@material/web/fab/fab.js';
|
||
import '@material/web/fab/branded-fab.js';
|
||
|
||
// Badges
|
||
import '@material/web/labs/badge/badge.js';
|
||
|
||
// Divider
|
||
import '@material/web/divider/divider.js';
|
||
|
||
// Tabs
|
||
import '@material/web/tabs/tabs.js';
|
||
import '@material/web/tabs/primary-tab.js';
|
||
import '@material/web/tabs/secondary-tab.js';
|
||
```
|
||
|
||
Импортировать в `app/layout.tsx`:
|
||
|
||
```typescript
|
||
import '@/lib/material-components';
|
||
```
|
||
|
||
---
|
||
|
||
## 🎨 Material Design 3 Grid System
|
||
|
||
Создать файл `styles/material-grid.css`:
|
||
|
||
```css
|
||
/* Material Design 3 Layout Grid */
|
||
|
||
/* Breakpoints:
|
||
- xs: 0-599px (Mobile)
|
||
- sm: 600-839px (Tablet Portrait)
|
||
- md: 840-1239px (Tablet Landscape / Small Desktop)
|
||
- lg: 1240-1439px (Desktop)
|
||
- xl: 1440px+ (Large Desktop)
|
||
*/
|
||
|
||
.md-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 16px;
|
||
padding: 0 16px;
|
||
width: 100%;
|
||
}
|
||
|
||
/* Tablet Portrait (600-839px) */
|
||
@media (min-width: 600px) {
|
||
.md-grid {
|
||
grid-template-columns: repeat(8, 1fr);
|
||
gap: 24px;
|
||
padding: 0 24px;
|
||
}
|
||
}
|
||
|
||
/* Tablet Landscape / Desktop (840-1239px) */
|
||
@media (min-width: 840px) {
|
||
.md-grid {
|
||
grid-template-columns: repeat(12, 1fr);
|
||
gap: 24px;
|
||
padding: 0 24px;
|
||
}
|
||
}
|
||
|
||
/* Large Desktop (1240px+) */
|
||
@media (min-width: 1240px) {
|
||
.md-grid {
|
||
gap: 24px;
|
||
padding: 0 24px;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
}
|
||
|
||
/* Column Span Classes */
|
||
/* Mobile (4 columns) */
|
||
.md-col-1 { grid-column: span 1; }
|
||
.md-col-2 { grid-column: span 2; }
|
||
.md-col-3 { grid-column: span 3; }
|
||
.md-col-4 { grid-column: span 4; }
|
||
|
||
/* Tablet (8 columns) */
|
||
@media (min-width: 600px) {
|
||
.md-col-sm-1 { grid-column: span 1; }
|
||
.md-col-sm-2 { grid-column: span 2; }
|
||
.md-col-sm-4 { grid-column: span 4; }
|
||
.md-col-sm-6 { grid-column: span 6; }
|
||
.md-col-sm-8 { grid-column: span 8; }
|
||
}
|
||
|
||
/* Desktop (12 columns) */
|
||
@media (min-width: 840px) {
|
||
.md-col-md-3 { grid-column: span 3; }
|
||
.md-col-md-4 { grid-column: span 4; }
|
||
.md-col-md-6 { grid-column: span 6; }
|
||
.md-col-md-8 { grid-column: span 8; }
|
||
.md-col-md-9 { grid-column: span 9; }
|
||
.md-col-md-12 { grid-column: span 12; }
|
||
}
|
||
|
||
/* Flexbox альтернатива для простых случаев */
|
||
.md-flex {
|
||
display: flex;
|
||
gap: 16px;
|
||
}
|
||
|
||
.md-flex-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.md-flex-wrap {
|
||
flex-wrap: wrap;
|
||
}
|
||
```
|
||
|
||
**Использование:**
|
||
|
||
```tsx
|
||
<div className="md-grid">
|
||
<div className="md-col-4 md-col-sm-4 md-col-md-6">
|
||
<md-elevated-card>Карточка 1</md-elevated-card>
|
||
</div>
|
||
<div className="md-col-4 md-col-sm-4 md-col-md-6">
|
||
<md-elevated-card>Карточка 2</md-elevated-card>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
---
|
||
|
||
## 📱 Примеры использования компонентов
|
||
|
||
### 1. Форма входа
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
export default function LoginPage() {
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
// Логика входа
|
||
};
|
||
|
||
return (
|
||
<div className="md-grid" style={{ minHeight: '100vh', alignItems: 'center' }}>
|
||
<div className="md-col-4 md-col-sm-8 md-col-md-6" style={{ margin: '0 auto' }}>
|
||
<md-elevated-card style={{ padding: '32px' }}>
|
||
<h1 className="md-typescale-headline-medium" style={{ marginBottom: '24px' }}>
|
||
Вход в систему
|
||
</h1>
|
||
|
||
<form onSubmit={handleSubmit}>
|
||
<md-outlined-text-field
|
||
label="Email"
|
||
type="email"
|
||
required
|
||
style={{ width: '100%', marginBottom: '16px' }}
|
||
></md-outlined-text-field>
|
||
|
||
<md-outlined-text-field
|
||
label="Пароль"
|
||
type="password"
|
||
required
|
||
style={{ width: '100%', marginBottom: '24px' }}
|
||
></md-outlined-text-field>
|
||
|
||
<md-filled-button type="submit" style={{ width: '100%' }}>
|
||
Войти
|
||
</md-filled-button>
|
||
</form>
|
||
</md-elevated-card>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 2. Дашборд с карточками
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
export default function DashboardPage() {
|
||
return (
|
||
<div className="md-grid" style={{ padding: '24px 0' }}>
|
||
{/* Статистика */}
|
||
<div className="md-col-4 md-col-sm-4 md-col-md-4">
|
||
<md-elevated-card style={{ padding: '24px' }}>
|
||
<md-icon style={{ fontSize: '48px', color: 'var(--md-sys-color-primary)' }}>
|
||
groups
|
||
</md-icon>
|
||
<h2 className="md-typescale-headline-small">24</h2>
|
||
<p className="md-typescale-body-medium">Студентов</p>
|
||
</md-elevated-card>
|
||
</div>
|
||
|
||
<div className="md-col-4 md-col-sm-4 md-col-md-4">
|
||
<md-elevated-card style={{ padding: '24px' }}>
|
||
<md-icon style={{ fontSize: '48px', color: 'var(--md-sys-color-primary)' }}>
|
||
calendar_month
|
||
</md-icon>
|
||
<h2 className="md-typescale-headline-small">12</h2>
|
||
<p className="md-typescale-body-medium">Занятий на неделе</p>
|
||
</md-elevated-card>
|
||
</div>
|
||
|
||
<div className="md-col-4 md-col-sm-4 md-col-md-4">
|
||
<md-elevated-card style={{ padding: '24px' }}>
|
||
<md-icon style={{ fontSize: '48px', color: 'var(--md-sys-color-primary)' }}>
|
||
payments
|
||
</md-icon>
|
||
<h2 className="md-typescale-headline-small">45000₽</h2>
|
||
<p className="md-typescale-body-medium">Доход за месяц</p>
|
||
</md-elevated-card>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 3. Bottom Navigation Bar (iOS-style)
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
import { usePathname, useRouter } from 'next/navigation';
|
||
import { useEffect, useRef } from 'react';
|
||
|
||
export function BottomNavigationBar({ userRole }: { userRole: 'mentor' | 'client' | 'parent' }) {
|
||
const pathname = usePathname();
|
||
const router = useRouter();
|
||
const navRef = useRef<any>(null);
|
||
|
||
useEffect(() => {
|
||
const nav = navRef.current;
|
||
if (!nav) return;
|
||
|
||
const handleNavigation = (e: CustomEvent) => {
|
||
const activeIndex = e.detail.activeIndex;
|
||
const tabs = nav.querySelectorAll('md-navigation-tab');
|
||
const activeTab = tabs[activeIndex];
|
||
const href = activeTab?.getAttribute('data-href');
|
||
if (href) {
|
||
router.push(href);
|
||
}
|
||
};
|
||
|
||
nav.addEventListener('navigation-tab-interaction', handleNavigation);
|
||
return () => nav.removeEventListener('navigation-tab-interaction', handleNavigation);
|
||
}, [router]);
|
||
|
||
// Меню для ментора
|
||
if (userRole === 'mentor') {
|
||
return (
|
||
<md-navigation-bar ref={navRef} className="ios-bottom-bar">
|
||
<md-navigation-tab
|
||
label="Главная"
|
||
data-href="/dashboard"
|
||
active={pathname === '/dashboard'}
|
||
>
|
||
<md-icon slot="inactive-icon">home</md-icon>
|
||
<md-icon slot="active-icon">home</md-icon>
|
||
</md-navigation-tab>
|
||
|
||
<md-navigation-tab
|
||
label="Студенты"
|
||
data-href="/students"
|
||
active={pathname === '/students'}
|
||
>
|
||
<md-icon slot="inactive-icon">group</md-icon>
|
||
<md-icon slot="active-icon">group</md-icon>
|
||
</md-navigation-tab>
|
||
|
||
<md-navigation-tab
|
||
label="Расписание"
|
||
data-href="/schedule"
|
||
active={pathname === '/schedule'}
|
||
>
|
||
<md-icon slot="inactive-icon">calendar_month</md-icon>
|
||
<md-icon slot="active-icon">calendar_month</md-icon>
|
||
</md-navigation-tab>
|
||
|
||
<md-navigation-tab
|
||
label="Чат"
|
||
data-href="/chat"
|
||
active={pathname === '/chat'}
|
||
>
|
||
<md-icon slot="inactive-icon">chat</md-icon>
|
||
<md-icon slot="active-icon">chat</md-icon>
|
||
</md-navigation-tab>
|
||
</md-navigation-bar>
|
||
);
|
||
}
|
||
|
||
// Аналогично для client и parent...
|
||
return null;
|
||
}
|
||
```
|
||
|
||
### 4. Список студентов
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
export default function StudentsPage() {
|
||
return (
|
||
<div className="md-grid">
|
||
<div className="md-col-4 md-col-sm-8 md-col-md-12">
|
||
<md-list>
|
||
<md-list-item>
|
||
<md-icon slot="start">person</md-icon>
|
||
<div slot="headline">Иван Иванов</div>
|
||
<div slot="supporting-text">ivan@example.com</div>
|
||
<md-icon-button slot="end">
|
||
<md-icon>more_vert</md-icon>
|
||
</md-icon-button>
|
||
</md-list-item>
|
||
|
||
<md-divider></md-divider>
|
||
|
||
<md-list-item>
|
||
<md-icon slot="start">person</md-icon>
|
||
<div slot="headline">Петр Петров</div>
|
||
<div slot="supporting-text">petr@example.com</div>
|
||
<md-icon-button slot="end">
|
||
<md-icon>more_vert</md-icon>
|
||
</md-icon-button>
|
||
</md-list-item>
|
||
</md-list>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 5. Диалог (модальное окно)
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
import { useRef } from 'react';
|
||
|
||
export function CreateLessonDialog() {
|
||
const dialogRef = useRef<any>(null);
|
||
|
||
const openDialog = () => {
|
||
dialogRef.current?.show();
|
||
};
|
||
|
||
const closeDialog = () => {
|
||
dialogRef.current?.close();
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<md-filled-button onClick={openDialog}>
|
||
Создать занятие
|
||
</md-filled-button>
|
||
|
||
<md-dialog ref={dialogRef}>
|
||
<div slot="headline">Новое занятие</div>
|
||
<form slot="content" method="dialog">
|
||
<md-outlined-text-field
|
||
label="Название"
|
||
required
|
||
style={{ width: '100%', marginBottom: '16px' }}
|
||
></md-outlined-text-field>
|
||
|
||
<md-outlined-text-field
|
||
label="Дата"
|
||
type="date"
|
||
required
|
||
style={{ width: '100%', marginBottom: '16px' }}
|
||
></md-outlined-text-field>
|
||
</form>
|
||
<div slot="actions">
|
||
<md-text-button onClick={closeDialog}>Отмена</md-text-button>
|
||
<md-filled-button onClick={closeDialog}>Создать</md-filled-button>
|
||
</div>
|
||
</md-dialog>
|
||
</>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 6. Chips (фильтры)
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
export function FiltersBar() {
|
||
return (
|
||
<md-chip-set>
|
||
<md-filter-chip label="Все занятия"></md-filter-chip>
|
||
<md-filter-chip label="Активные"></md-filter-chip>
|
||
<md-filter-chip label="Завершенные"></md-filter-chip>
|
||
<md-filter-chip label="Отмененные"></md-filter-chip>
|
||
</md-chip-set>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 7. Прогресс индикаторы
|
||
|
||
```tsx
|
||
'use client';
|
||
|
||
export function LoadingIndicator() {
|
||
return (
|
||
<div style={{ display: 'flex', justifyContent: 'center', padding: '48px' }}>
|
||
<md-circular-progress indeterminate></md-circular-progress>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function UploadProgress({ value }: { value: number }) {
|
||
return (
|
||
<md-linear-progress value={value / 100}></md-linear-progress>
|
||
);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎨 Кастомизация под iOS 24+ стиль
|
||
|
||
Создать файл `styles/ios-material.css`:
|
||
|
||
```css
|
||
/* iOS 24+ адаптация для Material компонентов */
|
||
|
||
/* Bottom Navigation Bar */
|
||
md-navigation-bar {
|
||
--md-navigation-bar-container-color: rgba(255, 255, 255, 0.8);
|
||
--md-navigation-bar-container-height: 80px;
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
border-top: 0.5px solid rgba(0, 0, 0, 0.1);
|
||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.08);
|
||
border-radius: 24px 24px 0 0;
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1000;
|
||
}
|
||
|
||
[data-theme="dark"] md-navigation-bar {
|
||
--md-navigation-bar-container-color: rgba(28, 28, 30, 0.9);
|
||
border-top-color: rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
/* Cards с blur эффектом */
|
||
md-elevated-card {
|
||
--md-elevated-card-container-color: rgba(255, 255, 255, 0.8);
|
||
--md-elevated-card-container-shape: 20px;
|
||
backdrop-filter: blur(20px);
|
||
-webkit-backdrop-filter: blur(20px);
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
[data-theme="dark"] md-elevated-card {
|
||
--md-elevated-card-container-color: rgba(28, 28, 30, 0.8);
|
||
}
|
||
|
||
/* Buttons с iOS стилем */
|
||
md-filled-button {
|
||
--md-filled-button-container-shape: 16px;
|
||
--md-filled-button-container-height: 48px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Text Fields с iOS стилем */
|
||
md-outlined-text-field {
|
||
--md-outlined-text-field-container-shape: 12px;
|
||
--md-outlined-text-field-outline-width: 1px;
|
||
}
|
||
|
||
/* List Items с отступами */
|
||
md-list-item {
|
||
--md-list-item-container-shape: 12px;
|
||
margin: 4px 8px;
|
||
}
|
||
|
||
/* Dialogs с rounded corners */
|
||
md-dialog {
|
||
--md-dialog-container-shape: 24px;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🌈 Material Icons
|
||
|
||
Использовать Material Symbols из Google Fonts:
|
||
|
||
```html
|
||
<!-- В app/layout.tsx или в head -->
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
|
||
rel="stylesheet"
|
||
/>
|
||
```
|
||
|
||
**Использование:**
|
||
|
||
```tsx
|
||
<md-icon>home</md-icon>
|
||
<md-icon>calendar_month</md-icon>
|
||
<md-icon>chat</md-icon>
|
||
<md-icon>group</md-icon>
|
||
<md-icon>settings</md-icon>
|
||
```
|
||
|
||
**Список популярных иконок:**
|
||
- `home` - Главная
|
||
- `calendar_month` - Календарь
|
||
- `chat` - Чат
|
||
- `group` - Студенты/Группы
|
||
- `person` - Профиль
|
||
- `settings` - Настройки
|
||
- `notifications` - Уведомления
|
||
- `payment` - Оплата
|
||
- `school` - Обучение
|
||
- `video_call` - Видеозвонок
|
||
- `assignment` - Задания
|
||
- `folder` - Материалы
|
||
|
||
---
|
||
|
||
## 📋 Типографика Material Design 3
|
||
|
||
```html
|
||
<!-- Заголовки -->
|
||
<h1 className="md-typescale-display-large">Display Large</h1>
|
||
<h2 className="md-typescale-display-medium">Display Medium</h2>
|
||
<h3 className="md-typescale-display-small">Display Small</h3>
|
||
|
||
<h1 className="md-typescale-headline-large">Headline Large</h1>
|
||
<h2 className="md-typescale-headline-medium">Headline Medium</h2>
|
||
<h3 className="md-typescale-headline-small">Headline Small</h3>
|
||
|
||
<!-- Основной текст -->
|
||
<p className="md-typescale-body-large">Body Large</p>
|
||
<p className="md-typescale-body-medium">Body Medium</p>
|
||
<p className="md-typescale-body-small">Body Small</p>
|
||
|
||
<!-- Подписи -->
|
||
<p className="md-typescale-label-large">Label Large</p>
|
||
<p className="md-typescale-label-medium">Label Medium</p>
|
||
<p className="md-typescale-label-small">Label Small</p>
|
||
```
|
||
|
||
Импортировать типографику:
|
||
|
||
```typescript
|
||
import { styles as typescaleStyles } from '@material/web/typography/md-typescale-styles.js';
|
||
|
||
// В layout.tsx
|
||
useEffect(() => {
|
||
document.adoptedStyleSheets.push(typescaleStyles.styleSheet);
|
||
}, []);
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 Важные замечания
|
||
|
||
1. **Web Components в React:**
|
||
- Material Web Components - это нативные Web Components
|
||
- Используются напрямую в JSX как HTML элементы
|
||
- Не нужны wrapper компоненты
|
||
|
||
2. **События:**
|
||
- Используйте `ref` для доступа к элементу
|
||
- Добавляйте слушатели событий через `addEventListener`
|
||
|
||
3. **Стилизация:**
|
||
- CSS Variables для кастомизации
|
||
- Только чистый CSS, без Tailwind
|
||
- Material Grid System для layout
|
||
|
||
4. **Компоненты из `labs`:**
|
||
- Некоторые компоненты находятся в `@material/web/labs/`
|
||
- Например: `navigation-bar`, `badge`, `card`
|
||
|
||
---
|
||
|
||
**Документация:** https://github.com/material-components/material-web
|
||
**Demo:** https://material-web.dev/
|