Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,330 @@
|
||||
# ADR-005: Design Tokens как единый источник стилей
|
||||
|
||||
**Статус:** принято
|
||||
**Дата:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## Контекст
|
||||
|
||||
Проект VoIdea работает на нескольких платформах:
|
||||
- Web (PWA)
|
||||
- iOS (Swift/SwiftUI)
|
||||
- Android (Kotlin)
|
||||
|
||||
Требуется унифицировать стили (цвета, шрифты, отступы) между всеми платформами.
|
||||
|
||||
Рассматривались:
|
||||
- **Ручное копирование** — непоследовательно, сложно поддерживать
|
||||
- **Shared library** — требует синхронизации
|
||||
- **Design Tokens (JSON)** — единый источник, генераторы
|
||||
|
||||
---
|
||||
|
||||
## Решение
|
||||
|
||||
**Design Tokens в JSON + генераторы для каждой платформы**
|
||||
|
||||
```
|
||||
docs/design-system/tokens.json (источник истины)
|
||||
│
|
||||
├── generators/css_generator.py → app/design-tokens/css/theme.css
|
||||
├── generators/swift_generator.py → app/design-tokens/swift/Colors.swift
|
||||
└── generators/kotlin_generator.py → app/design-tokens/kotlin/colors.xml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Структура tokens.json
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"project": "VoIdea",
|
||||
"themes": ["system", "dark", "light"],
|
||||
|
||||
"colors": {
|
||||
"primary": {
|
||||
"500": "#6366F1",
|
||||
"600": "#4F46E5",
|
||||
"default": "#6366F1",
|
||||
"hover": "#4F46E5"
|
||||
},
|
||||
"background": {
|
||||
"system": "auto",
|
||||
"dark": "#0F172A",
|
||||
"light": "#FFFFFF"
|
||||
},
|
||||
"semantic": {
|
||||
"error": "#EF4444",
|
||||
"warning": "#F59E0B",
|
||||
"success": "#22C55E",
|
||||
"info": "#3B82F6"
|
||||
}
|
||||
},
|
||||
|
||||
"typography": {
|
||||
"font_family": {
|
||||
"primary": "Inter, system-ui, sans-serif"
|
||||
},
|
||||
"size": {
|
||||
"xs": "0.75rem",
|
||||
"sm": "0.875rem",
|
||||
"base": "1rem"
|
||||
}
|
||||
},
|
||||
|
||||
"spacing": {
|
||||
"xs": "0.25rem",
|
||||
"sm": "0.5rem",
|
||||
"md": "1rem",
|
||||
"lg": "1.5rem"
|
||||
},
|
||||
|
||||
"border_radius": {
|
||||
"sm": "0.25rem",
|
||||
"md": "0.5rem",
|
||||
"lg": "0.75rem"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Генераторы
|
||||
|
||||
### CSS Generator
|
||||
|
||||
```python
|
||||
# docs/design-system/generators/css_generator.py
|
||||
|
||||
def generate(tokens: dict) -> str:
|
||||
css = ":root {\n"
|
||||
|
||||
for category, values in tokens.items():
|
||||
if category == "colors":
|
||||
for name, value in flatten(values):
|
||||
css += f" --color-{name}: {value};\n"
|
||||
|
||||
elif category == "typography":
|
||||
for name, value in flatten(values):
|
||||
css += f" --font-{name}: {value};\n"
|
||||
|
||||
elif category == "spacing":
|
||||
for name, value in flatten(values):
|
||||
css += f" --spacing-{name}: {value};\n"
|
||||
|
||||
css += "}\n"
|
||||
return css
|
||||
```
|
||||
|
||||
**Выход:** `app/design-tokens/css/theme.css`
|
||||
|
||||
```css
|
||||
:root {
|
||||
--color-primary: #6366F1;
|
||||
--color-background-dark: #0F172A;
|
||||
--font-family-primary: Inter, system-ui, sans-serif;
|
||||
--spacing-md: 1rem;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Swift Generator
|
||||
|
||||
```python
|
||||
# docs/design-system/generators/swift_generator.py
|
||||
|
||||
def generate(tokens: dict) -> str:
|
||||
swift = "import SwiftUI\n\n"
|
||||
swift += "enum Colors {\n"
|
||||
|
||||
for name, value in flatten(tokens["colors"]):
|
||||
snake_to_camel = to_camel_case(name)
|
||||
swift += f' static let {snake_to_camel} = Color(hex: "{value}")\n'
|
||||
|
||||
swift += "}\n"
|
||||
swift += "enum Typography {\n"
|
||||
|
||||
# ... font generation
|
||||
|
||||
return swift
|
||||
```
|
||||
|
||||
**Выход:** `app/design-tokens/swift/Colors.swift`
|
||||
|
||||
```swift
|
||||
import SwiftUI
|
||||
|
||||
enum Colors {
|
||||
static let primary = Color(hex: "#6366F1")
|
||||
static let backgroundDark = Color(hex: "#0F172A")
|
||||
}
|
||||
|
||||
enum Typography {
|
||||
static let fontFamilyPrimary = "Inter"
|
||||
static let fontSizeBase: CGFloat = 16
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Kotlin Generator
|
||||
|
||||
```python
|
||||
# docs/design-system/generators/kotlin_generator.py
|
||||
|
||||
def generate(tokens: dict) -> str:
|
||||
xml = '<?xml version="1.0" encoding="utf-8"?>\n'
|
||||
xml += '<resources>\n'
|
||||
|
||||
for name, value in flatten(tokens["colors"]):
|
||||
safe_name = name.replace("_", "_")
|
||||
xml += f' <color name="{safe_name}">{value}</color>\n'
|
||||
|
||||
xml += '</resources>\n'
|
||||
return xml
|
||||
```
|
||||
|
||||
**Выход:** `app/design-tokens/kotlin/colors.xml`
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="primary">#6366F1</color>
|
||||
<color name="background_dark">#0F172A</color>
|
||||
</resources>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Темы
|
||||
|
||||
### System (Auto)
|
||||
|
||||
```css
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root[data-theme="system"] {
|
||||
--color-background: #0F172A;
|
||||
--color-text: #F8FAFC;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root[data-theme="system"] {
|
||||
--color-background: #FFFFFF;
|
||||
--color-text: #0F172A;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dark / Light
|
||||
|
||||
```css
|
||||
[data-theme="dark"] {
|
||||
--color-background: #0F172A;
|
||||
--color-text: #F8FAFC;
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--color-background: #FFFFFF;
|
||||
--color-text: #0F172A;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
```yaml
|
||||
# .github/workflows/design-system.yml
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/design-system/tokens.json'
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Generate CSS
|
||||
run: python -m generators css
|
||||
|
||||
- name: Generate Swift
|
||||
run: python -m generators swift
|
||||
|
||||
- name: Generate Kotlin
|
||||
run: python -m generators kotlin
|
||||
|
||||
- name: Commit
|
||||
run: |
|
||||
git add app/design-tokens/
|
||||
git commit -m "style: regenerate design tokens"
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Правила использования
|
||||
|
||||
1. **Никогда не редактировать сгенерированные файлы вручную**
|
||||
2. **Все изменения только в tokens.json**
|
||||
3. **После изменения tokens.json → запустить генераторы**
|
||||
4. **DocAgent обновляет документацию**
|
||||
|
||||
---
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Обновление tokens.json
|
||||
|
||||
1. Открыть `docs/design-system/tokens.json`
|
||||
2. Внести изменения
|
||||
3. Запустить `python -m generators all`
|
||||
4. Проверить сгенерированные файлы
|
||||
5. Зафиксировать в git
|
||||
|
||||
### Добавление новых token
|
||||
|
||||
```json
|
||||
{
|
||||
"new_token": {
|
||||
"system": "auto",
|
||||
"dark": "#value",
|
||||
"light": "#value"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Последствия
|
||||
|
||||
### Положительные
|
||||
|
||||
- Единый источник истины
|
||||
- Согласованность между платформами
|
||||
- Легко добавлять темы
|
||||
- Автоматическая генерация
|
||||
|
||||
### Отрицательные
|
||||
|
||||
- Дополнительный слой абстракции
|
||||
- Требует генерацию при изменениях
|
||||
- Разные форматы вывода
|
||||
|
||||
---
|
||||
|
||||
## Ответственный
|
||||
|
||||
**Decision maker:** Frontend team
|
||||
**Review date:** При добавлении новой платформы
|
||||
|
||||
---
|
||||
|
||||
*Создано: 2026-05-10*
|
||||
*Обновляется DocAgent при изменениях*
|
||||
Reference in New Issue
Block a user