@xetwa/design-system 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +30 -0
- package/.storybook/main.ts +17 -0
- package/.storybook/preview.tsx +65 -0
- package/eslint.config.js +22 -0
- package/index.html +13 -0
- package/package.json +55 -0
- package/public/favicon.svg +1 -0
- package/public/icons.svg +24 -0
- package/src/App.css +184 -0
- package/src/App.tsx +122 -0
- package/src/BordasSombras.mdx +343 -0
- package/src/Colors.mdx +393 -0
- package/src/GrelhaLayout.mdx +375 -0
- package/src/Icons.mdx +42 -0
- package/src/MovimentoBreakpoints.mdx +379 -0
- package/src/Spacing.mdx +231 -0
- package/src/Typography.mdx +321 -0
- package/src/assets/Mascots/vava/estaticos/vava-acenar.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-acertou.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-celebrar.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-com-autocolantes.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-dormir.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-errou.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-feliz.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-neutra.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-original.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-pensar.svg +3 -0
- package/src/assets/Mascots/vava/estaticos/vava-surpresa.svg +3 -0
- package/src/assets/Mascots/yaya/avatar/yaya-avatar-falar.svg +7 -0
- package/src/assets/Mascots/yaya/avatar/yaya-avatar-feliz.svg +7 -0
- package/src/assets/Mascots/yaya/avatar/yaya-avatar-transparente.svg +6 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-acertou.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-celebrar.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-dormir.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-errou.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-falar.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-feliz.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-neutra.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-pensar.svg +13 -0
- package/src/assets/Mascots/yaya/estaticos/yaya-surpresa.svg +13 -0
- package/src/assets/hero.png +0 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/vite.svg +1 -0
- package/src/components/Buttons/BackButton/BackButton.stories.tsx +86 -0
- package/src/components/Buttons/BackButton/BackButton.tsx +107 -0
- package/src/components/Buttons/FabButton/FabButton.stories.tsx +74 -0
- package/src/components/Buttons/FabButton/FabButton.tsx +161 -0
- package/src/components/Buttons/IconButton/IconButton.stories.tsx +99 -0
- package/src/components/Buttons/IconButton/IconButton.tsx +189 -0
- package/src/components/Buttons/JourneyButton/JourneyButton.stories.tsx +80 -0
- package/src/components/Buttons/JourneyButton/JourneyButton.tsx +194 -0
- package/src/components/Buttons/MainButton/MainButton.stories.tsx +144 -0
- package/src/components/Buttons/MainButton/MainButton.tsx +258 -0
- package/src/components/Buttons/WordChip/WordChip.stories.tsx +84 -0
- package/src/components/Buttons/WordChip/WordChip.tsx +157 -0
- package/src/components/Buttons/WordChip/index.ts +1 -0
- package/src/components/Cards/AnswerOptionCard/AnswerOptionCard.stories.tsx +181 -0
- package/src/components/Cards/AnswerOptionCard/AnswerOptionCard.tsx +201 -0
- package/src/components/Cards/BaseCard/BaseCard.stories.tsx +148 -0
- package/src/components/Cards/BaseCard/BaseCard.tsx +181 -0
- package/src/components/Cards/BoardingPassCard/BoardingPassCard.stories.tsx +205 -0
- package/src/components/Cards/BoardingPassCard/BoardingPassCard.tsx +384 -0
- package/src/components/Cards/BoardingPassCard/FlightIcon.tsx +21 -0
- package/src/components/Cards/BoardingPassCard/FlightIcon.web.tsx +18 -0
- package/src/components/Cards/CountryCard/CountryCard.stories.tsx +97 -0
- package/src/components/Cards/CountryCard/CountryCard.tsx +185 -0
- package/src/components/Cards/LevelProgressCard/LevelProgressCard.stories.tsx +61 -0
- package/src/components/Cards/LevelProgressCard/LevelProgressCard.tsx +143 -0
- package/src/components/Cards/ModuleCard/ModuleCard.stories.tsx +108 -0
- package/src/components/Cards/ModuleCard/ModuleCard.tsx +304 -0
- package/src/components/Cards/ModuleCompletionCard/ModuleCompletionCard.stories.tsx +53 -0
- package/src/components/Cards/ModuleCompletionCard/ModuleCompletionCard.tsx +167 -0
- package/src/components/Cards/Passport/PassportCover.tsx +114 -0
- package/src/components/Cards/Passport/PassportIDPage.tsx +217 -0
- package/src/components/Cards/Passport/PassportStampPage.tsx +253 -0
- package/src/components/Cards/Passport/PassportViewer.stories.tsx +95 -0
- package/src/components/Cards/Passport/PassportViewer.tsx +328 -0
- package/src/components/Cards/PlanCard/PlanCard.stories.tsx +159 -0
- package/src/components/Cards/PlanCard/PlanCard.tsx +317 -0
- package/src/components/Cards/PricingCard/PricingCard.stories.tsx +83 -0
- package/src/components/Cards/PricingCard/PricingCard.tsx +144 -0
- package/src/components/Cards/SelectionCard/SelectionCard.stories.tsx +77 -0
- package/src/components/Cards/SelectionCard/SelectionCard.tsx +111 -0
- package/src/components/Cards/TeacherCard/TeacherCard.stories.tsx +126 -0
- package/src/components/Cards/TeacherCard/TeacherCard.tsx +236 -0
- package/src/components/Charts/WeeklyProgressChart/WeeklyProgressChart.stories.tsx +89 -0
- package/src/components/Charts/WeeklyProgressChart/WeeklyProgressChart.tsx +179 -0
- package/src/components/Charts/WeeklyProgressChart/index.ts +1 -0
- package/src/components/DataDisplay/Avatar/Avatar.stories.tsx +85 -0
- package/src/components/DataDisplay/Avatar/Avatar.tsx +118 -0
- package/src/components/DataDisplay/Avatar/index.ts +1 -0
- package/src/components/DataDisplay/Badge/Badge.stories.tsx +62 -0
- package/src/components/DataDisplay/Badge/Badge.tsx +146 -0
- package/src/components/DataDisplay/Flag/Flag.stories.tsx +98 -0
- package/src/components/DataDisplay/Flag/Flag.tsx +347 -0
- package/src/components/DataDisplay/Flag/Flag.web.tsx +334 -0
- package/src/components/DataDisplay/InstructionBubble/InstructionBubble.stories.tsx +80 -0
- package/src/components/DataDisplay/InstructionBubble/InstructionBubble.tsx +86 -0
- package/src/components/DataDisplay/LevelBadge/LevelBadge.stories.tsx +99 -0
- package/src/components/DataDisplay/LevelBadge/LevelBadge.tsx +118 -0
- package/src/components/DataDisplay/ProgressBar/ProgressBar.stories.tsx +95 -0
- package/src/components/DataDisplay/ProgressBar/ProgressBar.tsx +117 -0
- package/src/components/DataDisplay/StatBadge/StatBadge.stories.tsx +82 -0
- package/src/components/DataDisplay/StatBadge/StatBadge.tsx +135 -0
- package/src/components/DataDisplay/StatBadge/index.ts +1 -0
- package/src/components/DataDisplay/Tag/Tag.stories.tsx +63 -0
- package/src/components/DataDisplay/Tag/Tag.tsx +148 -0
- package/src/components/Feedback/FeedbackBottomSheet/FeedbackBottomSheet.stories.tsx +74 -0
- package/src/components/Feedback/FeedbackBottomSheet/FeedbackBottomSheet.tsx +200 -0
- package/src/components/Forms/Checkbox/Checkbox.stories.tsx +60 -0
- package/src/components/Forms/Checkbox/Checkbox.tsx +127 -0
- package/src/components/Forms/Checkbox/index.ts +1 -0
- package/src/components/Forms/DaySelector/DaySelector.stories.tsx +62 -0
- package/src/components/Forms/DaySelector/DaySelector.tsx +133 -0
- package/src/components/Forms/Radio/Radio.stories.tsx +61 -0
- package/src/components/Forms/Radio/Radio.tsx +141 -0
- package/src/components/Forms/Radio/index.ts +1 -0
- package/src/components/Forms/SegmentedToggle/SegmentedToggle.stories.tsx +67 -0
- package/src/components/Forms/SegmentedToggle/SegmentedToggle.tsx +150 -0
- package/src/components/Forms/SegmentedToggle/index.ts +1 -0
- package/src/components/Forms/Select/Select.stories.tsx +59 -0
- package/src/components/Forms/Select/Select.tsx +255 -0
- package/src/components/Forms/Select/index.ts +1 -0
- package/src/components/Forms/Switch/Switch.stories.tsx +57 -0
- package/src/components/Forms/Switch/Switch.tsx +120 -0
- package/src/components/Forms/Switch/index.ts +1 -0
- package/src/components/Forms/TimeSlot/TimeSlot.stories.tsx +62 -0
- package/src/components/Forms/TimeSlot/TimeSlot.tsx +116 -0
- package/src/components/Headers/CountryHeader/CountryHeader.stories.tsx +114 -0
- package/src/components/Headers/CountryHeader/CountryHeader.tsx +147 -0
- package/src/components/Headers/ProfileHeader/ProfileHeader.stories.tsx +141 -0
- package/src/components/Headers/ProfileHeader/ProfileHeader.tsx +233 -0
- package/src/components/Inputs/FillInTheBlank/FillInTheBlank.stories.tsx +333 -0
- package/src/components/Inputs/FillInTheBlank/FillInTheBlank.tsx +287 -0
- package/src/components/Inputs/FillInTheBlank/index.ts +1 -0
- package/src/components/Inputs/TextInput/TextInput.stories.tsx +103 -0
- package/src/components/Inputs/TextInput/TextInput.tsx +187 -0
- package/src/components/Mascots/Vava/Vava.stories.tsx +114 -0
- package/src/components/Mascots/Vava/Vava.tsx +276 -0
- package/src/components/Mascots/Yaya/Yaya.stories.tsx +98 -0
- package/src/components/Mascots/Yaya/Yaya.tsx +233 -0
- package/src/components/Navigation/BottomTabBar/BottomTabBar.stories.tsx +68 -0
- package/src/components/Navigation/BottomTabBar/BottomTabBar.tsx +85 -0
- package/src/components/Navigation/BottomTabBar/BottomTabBarItem.tsx +68 -0
- package/src/components/Navigation/BottomTabBar/index.ts +2 -0
- package/src/components/Navigation/PaginationDots/PaginationDots.stories.tsx +62 -0
- package/src/components/Navigation/PaginationDots/PaginationDots.tsx +81 -0
- package/src/components/Navigation/PaginationDots/index.ts +1 -0
- package/src/components/Navigation/ProgressMap/ProgressMap.stories.tsx +116 -0
- package/src/components/Navigation/ProgressMap/ProgressMap.tsx +827 -0
- package/src/components/Navigation/WorldProgressMap/WorldProgressMap.stories.tsx +169 -0
- package/src/components/Navigation/WorldProgressMap/WorldProgressMap.tsx +790 -0
- package/src/components/Typography/Typography.stories.tsx +127 -0
- package/src/components/Typography/Typography.tsx +53 -0
- package/src/hooks/useResponsive.ts +41 -0
- package/src/hooks/useTheme.ts +32 -0
- package/src/index.css +355 -0
- package/src/index.ts +28 -0
- package/src/main.tsx +10 -0
- package/src/mocks/codegenNativeComponent.ts +5 -0
- package/src/styles/countryThemes.ts +79 -0
- package/src/styles/theme.ts +297 -0
- package/src/utils/iconMap.ts +23 -0
- package/tsconfig.app.json +25 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +24 -0
- package/vite.config.ts +51 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Brand Guidelines/Grelha & Layout" />
|
|
4
|
+
|
|
5
|
+
# Grelha & Layout
|
|
6
|
+
|
|
7
|
+
O layout do **Xetlingo** foi desenhado para garantir que a interface seja adaptável, organizada e acessível tanto em dispositivos móveis quanto em desktops e tablets. O nosso sistema baseia-se numa grelha fluida estruturada por colunas e limites máximos de conteúdo.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 📐 Regras de Grelha
|
|
12
|
+
|
|
13
|
+
* **Mobile (xs a md)**: Grelha de **4 colunas** com gutter adaptável. O conteúdo principal estende-se até às margens laterais seguras.
|
|
14
|
+
* **Desktop (xl a xxxl)**: Grelha de **12 colunas**. Para evitar esticamento excessivo em ecrãs ultra-wide, o conteúdo principal é centrado e limitado a um `max-width` de **1200px** com padding horizontal de **48px**.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 📱 Áreas de Layout (Mobile)
|
|
19
|
+
|
|
20
|
+
A estrutura vertical em telemóveis divide-se em zonas específicas para garantir uma navegação ergonómica e sem sobreposição de elementos do sistema operacional:
|
|
21
|
+
|
|
22
|
+
<div style={{
|
|
23
|
+
backgroundColor: 'var(--bg-surface)',
|
|
24
|
+
border: '1px solid var(--border-color)',
|
|
25
|
+
borderRadius: '16px',
|
|
26
|
+
padding: '1.5rem',
|
|
27
|
+
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03)',
|
|
28
|
+
marginBottom: '2.5rem',
|
|
29
|
+
marginTop: '1.5rem'
|
|
30
|
+
}}>
|
|
31
|
+
<table style={{
|
|
32
|
+
width: '100%',
|
|
33
|
+
borderCollapse: 'collapse',
|
|
34
|
+
textAlign: 'left'
|
|
35
|
+
}}>
|
|
36
|
+
<thead>
|
|
37
|
+
<tr style={{
|
|
38
|
+
borderBottom: '2px solid var(--border-color)',
|
|
39
|
+
}}>
|
|
40
|
+
<th style={{ padding: '10px 16px', fontSize: '0.75rem', fontWeight: '700', color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Zona</th>
|
|
41
|
+
<th style={{ padding: '10px 16px', fontSize: '0.75rem', fontWeight: '700', color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Altura</th>
|
|
42
|
+
<th style={{ padding: '10px 16px', fontSize: '0.75rem', fontWeight: '700', color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Notas de Implementação</th>
|
|
43
|
+
</tr>
|
|
44
|
+
</thead>
|
|
45
|
+
<tbody>
|
|
46
|
+
<tr style={{ borderBottom: '1px solid var(--border-color)' }}>
|
|
47
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Status bar (sistema)</td>
|
|
48
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
49
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>44–54px</code>
|
|
50
|
+
</td>
|
|
51
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
52
|
+
Espaço nativo do OS. Nunca colocar conteúdo interativo ou textos da app aqui.
|
|
53
|
+
</td>
|
|
54
|
+
</tr>
|
|
55
|
+
<tr style={{ borderBottom: '1px solid var(--border-color)' }}>
|
|
56
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Navigation header</td>
|
|
57
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
58
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>52px</code>
|
|
59
|
+
</td>
|
|
60
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
61
|
+
Contém o logótipo/título e botões de ação. Adicionar <strong>safe area top</strong> (ex: +16px ou mais no iPhone com notch).
|
|
62
|
+
</td>
|
|
63
|
+
</tr>
|
|
64
|
+
<tr style={{ borderBottom: '1px solid var(--border-color)' }}>
|
|
65
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Conteúdo scrollável</td>
|
|
66
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
67
|
+
<code style={{ fontSize: '0.8rem' }}>flex: 1</code>
|
|
68
|
+
</td>
|
|
69
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
70
|
+
Área de conteúdo dinâmico. Ocupa todo o espaço vertical intermédio (com <code style={{ fontSize: '0.8rem' }}>overflow-y: auto</code> na web).
|
|
71
|
+
</td>
|
|
72
|
+
</tr>
|
|
73
|
+
<tr style={{ borderBottom: '1px solid var(--border-color)' }}>
|
|
74
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Bottom tab bar</td>
|
|
75
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
76
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>56px</code>
|
|
77
|
+
</td>
|
|
78
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
79
|
+
Barra inferior de abas. Contém de 4 a 5 ícones com respetiva label de 10px (<code style={{ fontSize: '0.8rem' }}>caption</code>) posicionada por baixo.
|
|
80
|
+
</td>
|
|
81
|
+
</tr>
|
|
82
|
+
<tr>
|
|
83
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Safe area bottom (iPhone)</td>
|
|
84
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
85
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>0–34px</code>
|
|
86
|
+
</td>
|
|
87
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
88
|
+
Nativo do OS. Usar <code style={{ fontSize: '0.8rem' }}>env(safe-area-inset-bottom)</code> para empurrar o conteúdo clicável e evitar conflito com o indicador de home do iOS.
|
|
89
|
+
</td>
|
|
90
|
+
</tr>
|
|
91
|
+
</tbody>
|
|
92
|
+
</table>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 💻 Áreas de Layout (Desktop & Tablet)
|
|
98
|
+
|
|
99
|
+
Em ecrãs maiores, o layout estende-se lateralmente para melhor aproveitamento do espaço horizontal:
|
|
100
|
+
|
|
101
|
+
<div style={{
|
|
102
|
+
backgroundColor: 'var(--bg-surface)',
|
|
103
|
+
border: '1px solid var(--border-color)',
|
|
104
|
+
borderRadius: '16px',
|
|
105
|
+
padding: '1.5rem',
|
|
106
|
+
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03)',
|
|
107
|
+
marginBottom: '2.5rem'
|
|
108
|
+
}}>
|
|
109
|
+
<table style={{
|
|
110
|
+
width: '100%',
|
|
111
|
+
borderCollapse: 'collapse',
|
|
112
|
+
textAlign: 'left'
|
|
113
|
+
}}>
|
|
114
|
+
<thead>
|
|
115
|
+
<tr style={{
|
|
116
|
+
borderBottom: '2px solid var(--border-color)',
|
|
117
|
+
}}>
|
|
118
|
+
<th style={{ padding: '10px 16px', fontSize: '0.75rem', fontWeight: '700', color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Zona</th>
|
|
119
|
+
<th style={{ padding: '10px 16px', fontSize: '0.75rem', fontWeight: '700', color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Largura / Altura</th>
|
|
120
|
+
<th style={{ padding: '10px 16px', fontSize: '0.75rem', fontWeight: '700', color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>Notas de Implementação</th>
|
|
121
|
+
</tr>
|
|
122
|
+
</thead>
|
|
123
|
+
<tbody>
|
|
124
|
+
<tr style={{ borderBottom: '1px solid var(--border-color)' }}>
|
|
125
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Sidebar nav (desktop)</td>
|
|
126
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
127
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>220px largura</code>
|
|
128
|
+
</td>
|
|
129
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
130
|
+
Menu lateral fixo (<code style={{ fontSize: '0.8rem' }}>position: sticky; top: 0</code>). Contém o logótipo no topo e grupos de navegação com ícone + label horizontal.
|
|
131
|
+
</td>
|
|
132
|
+
</tr>
|
|
133
|
+
<tr style={{ borderBottom: '1px solid var(--border-color)' }}>
|
|
134
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Top nav bar (tablet)</td>
|
|
135
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
136
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>64px altura</code> / <code style={{ fontSize: '0.8rem' }}>100% largura</code>
|
|
137
|
+
</td>
|
|
138
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
139
|
+
Barra superior de navegação para tablets em modo retrato ou paisagem estreito. Logótipo à esquerda, abas centrais e ações à direita.
|
|
140
|
+
</td>
|
|
141
|
+
</tr>
|
|
142
|
+
<tr>
|
|
143
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', fontWeight: '600', color: 'var(--text-primary)' }}>Conteúdo principal</td>
|
|
144
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
145
|
+
<code style={{ fontSize: '0.8rem', color: '#cf7a30', backgroundColor: '#fef6ee', padding: '2px 6px', borderRadius: '4px' }}>max-width: 1200px</code>
|
|
146
|
+
</td>
|
|
147
|
+
<td style={{ padding: '12px 16px', fontSize: '0.9rem', color: 'var(--text-primary)' }}>
|
|
148
|
+
Área de trabalho/estudo centralizada. Aplica <code style={{ fontSize: '0.8rem' }}>padding: 48px h</code> lateral. A grelha interna de 12 colunas fica restrita dentro deste limite.
|
|
149
|
+
</td>
|
|
150
|
+
</tr>
|
|
151
|
+
</tbody>
|
|
152
|
+
</table>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 🎨 Esqueleto Visual dos Layouts
|
|
158
|
+
|
|
159
|
+
Compara visualmente a anatomia de ecrãs estruturada em **Mobile** e **Desktop**:
|
|
160
|
+
|
|
161
|
+
<div style={{
|
|
162
|
+
display: 'flex',
|
|
163
|
+
flexWrap: 'wrap',
|
|
164
|
+
gap: '2.5rem',
|
|
165
|
+
marginTop: '1.5rem',
|
|
166
|
+
marginBottom: '2.5rem'
|
|
167
|
+
}}>
|
|
168
|
+
|
|
169
|
+
{/* Mobile Skeleton Mockup */}
|
|
170
|
+
<div style={{
|
|
171
|
+
flex: '1 1 300px',
|
|
172
|
+
maxWidth: '360px',
|
|
173
|
+
border: '4px solid #51332d',
|
|
174
|
+
borderRadius: '32px',
|
|
175
|
+
height: '600px',
|
|
176
|
+
backgroundColor: 'var(--bg-app)',
|
|
177
|
+
display: 'flex',
|
|
178
|
+
flexDirection: 'column',
|
|
179
|
+
overflow: 'hidden',
|
|
180
|
+
position: 'relative',
|
|
181
|
+
boxShadow: 'var(--shadow-lg)'
|
|
182
|
+
}}>
|
|
183
|
+
{/* Status Bar */}
|
|
184
|
+
<div style={{
|
|
185
|
+
height: '44px',
|
|
186
|
+
backgroundColor: 'rgba(81, 51, 45, 0.08)',
|
|
187
|
+
display: 'flex',
|
|
188
|
+
alignItems: 'center',
|
|
189
|
+
justifyContent: 'space-between',
|
|
190
|
+
padding: '0 20px',
|
|
191
|
+
fontSize: '0.75rem',
|
|
192
|
+
fontWeight: 'bold',
|
|
193
|
+
color: 'var(--text-secondary)',
|
|
194
|
+
borderBottom: '1px dashed var(--border-color)',
|
|
195
|
+
}}>
|
|
196
|
+
<span>9:41</span>
|
|
197
|
+
<span style={{ fontSize: '0.65rem' }}>🔋 📶</span>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
{/* Navigation Header */}
|
|
201
|
+
<div style={{
|
|
202
|
+
height: '52px',
|
|
203
|
+
backgroundColor: 'var(--bg-surface)',
|
|
204
|
+
borderBottom: '2px solid var(--border-color)',
|
|
205
|
+
display: 'flex',
|
|
206
|
+
alignItems: 'center',
|
|
207
|
+
padding: '0 16px',
|
|
208
|
+
gap: '12px'
|
|
209
|
+
}}>
|
|
210
|
+
<div style={{ width: '28px', height: '28px', borderRadius: '50%', backgroundColor: '#f2974b', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontSize: '0.7rem' }}>⬅</div>
|
|
211
|
+
<div style={{ fontFamily: 'var(--font-title)', fontWeight: 'bold', fontSize: '1rem', color: 'var(--text-primary)' }}>Xetlingo</div>
|
|
212
|
+
<div style={{ marginLeft: 'auto', fontSize: '1.1rem' }}>🔥 5</div>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
{/* Scrollable Content Mock */}
|
|
216
|
+
<div style={{
|
|
217
|
+
flex: 1,
|
|
218
|
+
padding: '16px',
|
|
219
|
+
overflowY: 'auto',
|
|
220
|
+
display: 'flex',
|
|
221
|
+
flexDirection: 'column',
|
|
222
|
+
gap: '12px'
|
|
223
|
+
}}>
|
|
224
|
+
<div style={{ backgroundColor: 'var(--bg-surface)', border: '1px solid var(--border-color)', borderRadius: '12px', padding: '12px', boxShadow: 'var(--shadow-xs)' }}>
|
|
225
|
+
<div style={{ width: '40%', height: '14px', backgroundColor: 'var(--border-color)', borderRadius: '4px', marginBottom: '8px' }} />
|
|
226
|
+
<div style={{ width: '90%', height: '10px', backgroundColor: 'var(--border-color)', opacity: 0.6, borderRadius: '4px', marginBottom: '6px' }} />
|
|
227
|
+
<div style={{ width: '75%', height: '10px', backgroundColor: 'var(--border-color)', opacity: 0.6, borderRadius: '4px' }} />
|
|
228
|
+
</div>
|
|
229
|
+
<div style={{ backgroundColor: 'var(--bg-surface)', border: '1px solid var(--border-color)', borderRadius: '12px', padding: '12px', boxShadow: 'var(--shadow-xs)' }}>
|
|
230
|
+
<div style={{ width: '50%', height: '14px', backgroundColor: 'var(--border-color)', borderRadius: '4px', marginBottom: '8px' }} />
|
|
231
|
+
<div style={{ width: '85%', height: '10px', backgroundColor: 'var(--border-color)', opacity: 0.6, borderRadius: '4px' }} />
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
{/* Visual columns indicator overlay */}
|
|
235
|
+
<div style={{ marginTop: 'auto', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '10px', opacity: 0.25 }}>
|
|
236
|
+
<div style={{ height: '50px', backgroundColor: '#f2974b', borderRadius: '4px' }} />
|
|
237
|
+
<div style={{ height: '50px', backgroundColor: '#f2974b', borderRadius: '4px' }} />
|
|
238
|
+
<div style={{ height: '50px', backgroundColor: '#f2974b', borderRadius: '4px' }} />
|
|
239
|
+
<div style={{ height: '50px', backgroundColor: '#f2974b', borderRadius: '4px' }} />
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
{/* Bottom Tab Bar */}
|
|
244
|
+
<div style={{
|
|
245
|
+
height: '56px',
|
|
246
|
+
backgroundColor: 'var(--bg-surface)',
|
|
247
|
+
borderTop: '2px solid var(--border-color)',
|
|
248
|
+
display: 'grid',
|
|
249
|
+
gridTemplateColumns: 'repeat(4, 1fr)',
|
|
250
|
+
alignItems: 'center',
|
|
251
|
+
justifyItems: 'center'
|
|
252
|
+
}}>
|
|
253
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
254
|
+
<span style={{ fontSize: '1.2rem', color: '#f2974b' }}>🏠</span>
|
|
255
|
+
<span style={{ fontSize: '8px', fontWeight: '800', textTransform: 'uppercase', color: '#f2974b', marginTop: '2px' }}>Home</span>
|
|
256
|
+
</div>
|
|
257
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', opacity: 0.5 }}>
|
|
258
|
+
<span style={{ fontSize: '1.2rem' }}>📖</span>
|
|
259
|
+
<span style={{ fontSize: '8px', fontWeight: '800', textTransform: 'uppercase', marginTop: '2px' }}>Aulas</span>
|
|
260
|
+
</div>
|
|
261
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', opacity: 0.5 }}>
|
|
262
|
+
<span style={{ fontSize: '1.2rem' }}>🏆</span>
|
|
263
|
+
<span style={{ fontSize: '8px', fontWeight: '800', textTransform: 'uppercase', marginTop: '2px' }}>Ligas</span>
|
|
264
|
+
</div>
|
|
265
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', opacity: 0.5 }}>
|
|
266
|
+
<span style={{ fontSize: '1.2rem' }}>👤</span>
|
|
267
|
+
<span style={{ fontSize: '8px', fontWeight: '800', textTransform: 'uppercase', marginTop: '2px' }}>Perfil</span>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
{/* Safe Area Bottom Slot */}
|
|
272
|
+
<div style={{
|
|
273
|
+
height: '24px',
|
|
274
|
+
backgroundColor: 'var(--bg-surface)',
|
|
275
|
+
display: 'flex',
|
|
276
|
+
alignItems: 'center',
|
|
277
|
+
justifyContent: 'center',
|
|
278
|
+
borderTop: '1px dashed var(--border-color)'
|
|
279
|
+
}}>
|
|
280
|
+
<div style={{ width: '120px', height: '5px', borderRadius: '10px', backgroundColor: '#51332d', opacity: 0.3 }} />
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
{/* Desktop Skeleton Mockup */}
|
|
285
|
+
<div style={{
|
|
286
|
+
flex: '1 1 500px',
|
|
287
|
+
minWidth: '460px',
|
|
288
|
+
border: '4px solid #51332d',
|
|
289
|
+
borderRadius: '16px',
|
|
290
|
+
height: '600px',
|
|
291
|
+
backgroundColor: 'var(--bg-app)',
|
|
292
|
+
display: 'flex',
|
|
293
|
+
overflow: 'hidden',
|
|
294
|
+
boxShadow: 'var(--shadow-lg)'
|
|
295
|
+
}}>
|
|
296
|
+
{/* Sidebar Nav */}
|
|
297
|
+
<div style={{
|
|
298
|
+
width: '180px',
|
|
299
|
+
backgroundColor: 'var(--bg-surface)',
|
|
300
|
+
borderRight: '2px solid var(--border-color)',
|
|
301
|
+
padding: '20px 12px',
|
|
302
|
+
display: 'flex',
|
|
303
|
+
flexDirection: 'column',
|
|
304
|
+
gap: '24px'
|
|
305
|
+
}}>
|
|
306
|
+
{/* Brand logo slot */}
|
|
307
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', padding: '0 8px' }}>
|
|
308
|
+
<div style={{ width: '28px', height: '28px', borderRadius: '8px', backgroundColor: '#f2974b' }} />
|
|
309
|
+
<span style={{ fontFamily: 'var(--font-title)', fontWeight: 'bold', fontSize: '1rem', color: 'var(--text-primary)' }}>Xetlingo</span>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* Nav items */}
|
|
313
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
|
314
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '8px', borderRadius: '8px', backgroundColor: '#fef6ee', color: '#f2974b', fontWeight: 'bold', fontSize: '0.85rem' }}>
|
|
315
|
+
<span>🏠</span> Painel
|
|
316
|
+
</div>
|
|
317
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '8px', borderRadius: '8px', color: 'var(--text-primary)', opacity: 0.7, fontSize: '0.85rem' }}>
|
|
318
|
+
<span>📖</span> Lições
|
|
319
|
+
</div>
|
|
320
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '8px', borderRadius: '8px', color: 'var(--text-primary)', opacity: 0.7, fontSize: '0.85rem' }}>
|
|
321
|
+
<span>🏆</span> Classificação
|
|
322
|
+
</div>
|
|
323
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '8px', borderRadius: '8px', color: 'var(--text-primary)', opacity: 0.7, fontSize: '0.85rem' }}>
|
|
324
|
+
<span>👤</span> Perfil
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
{/* Main area + header */}
|
|
330
|
+
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
331
|
+
{/* Top Nav Bar for desktop/tablet context */}
|
|
332
|
+
<div style={{
|
|
333
|
+
height: '64px',
|
|
334
|
+
backgroundColor: 'var(--bg-surface)',
|
|
335
|
+
borderBottom: '2px solid var(--border-color)',
|
|
336
|
+
display: 'flex',
|
|
337
|
+
alignItems: 'center',
|
|
338
|
+
padding: '0 24px',
|
|
339
|
+
justifyContent: 'flex-end',
|
|
340
|
+
gap: '16px'
|
|
341
|
+
}}>
|
|
342
|
+
<div style={{ fontSize: '0.85rem', fontWeight: 'bold', color: 'var(--text-secondary)' }}>🔥 14 Dias</div>
|
|
343
|
+
<div style={{ width: '32px', height: '32px', borderRadius: '50%', backgroundColor: '#efe2d6', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>🦉</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
{/* Work area grid demonstration */}
|
|
347
|
+
<div style={{ flex: 1, padding: '24px', overflowY: 'auto' }}>
|
|
348
|
+
<div style={{ maxWidth: '1200px', margin: '0 auto', display: 'flex', flexDirection: 'column', gap: '16px' }}>
|
|
349
|
+
<div style={{ height: '36px', width: '200px', backgroundColor: 'var(--border-color)', borderRadius: '6px' }} />
|
|
350
|
+
|
|
351
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(12, 1fr)', gap: '12px', marginTop: '12px' }}>
|
|
352
|
+
{Array.from({ length: 12 }).map((_, i) => (
|
|
353
|
+
<div key={i} style={{
|
|
354
|
+
height: '180px',
|
|
355
|
+
backgroundColor: 'rgba(242, 151, 75, 0.1)',
|
|
356
|
+
border: '1px dashed #f2974b',
|
|
357
|
+
borderRadius: '8px',
|
|
358
|
+
gridColumn: i < 8 ? 'span 4' : 'span 4',
|
|
359
|
+
display: 'flex',
|
|
360
|
+
alignItems: 'center',
|
|
361
|
+
justifyContent: 'center',
|
|
362
|
+
fontSize: '0.7rem',
|
|
363
|
+
color: '#cf7a30',
|
|
364
|
+
fontWeight: 'bold'
|
|
365
|
+
}}>
|
|
366
|
+
Col {i + 1}
|
|
367
|
+
</div>
|
|
368
|
+
))}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
</div>
|
package/src/Icons.mdx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Brand Guidelines/Ícones (Lucide)" />
|
|
4
|
+
|
|
5
|
+
# Biblioteca de Ícones Oficial
|
|
6
|
+
|
|
7
|
+
A biblioteca oficial e exclusiva de ícones para o Design System do Xetlingo é a **\`lucide-react-native\`**.
|
|
8
|
+
|
|
9
|
+
## Como Usar Ícones nos Componentes
|
|
10
|
+
|
|
11
|
+
Para facilitar a vida aos developers e garantir consistência, **não passamos elementos JSX completos** nas propriedades dos botões ou componentes base. Em vez disso, passamos apenas o **nome exato** do ícone em formato de string.
|
|
12
|
+
|
|
13
|
+
### A Regra de Ouro:
|
|
14
|
+
Os componentes (como o \`IconButton\`, \`FabButton\`, etc.) expõem uma propriedade chamada \`iconName\`. Deve preenchê-la com o nome exato (em PascalCase) com que o ícone é exportado pela biblioteca Lucide.
|
|
15
|
+
|
|
16
|
+
\`\`\`tsx
|
|
17
|
+
// ❌ ERRADO: Não passe o componente importado instanciado
|
|
18
|
+
import { Play } from 'lucide-react-native';
|
|
19
|
+
<IconButton icon={<Play size={24} color="#000" />} />
|
|
20
|
+
|
|
21
|
+
// ✅ CERTO: Passe apenas o nome do ícone (String)
|
|
22
|
+
<IconButton iconName="Play" />
|
|
23
|
+
<FabButton iconName="Volume2" />
|
|
24
|
+
<JourneyButton iconName="ArrowRight" />
|
|
25
|
+
\`\`\`
|
|
26
|
+
|
|
27
|
+
## Onde encontrar os ícones disponíveis?
|
|
28
|
+
Pode pesquisar todos os ícones disponíveis e os seus nomes na documentação oficial do projeto Lucide:
|
|
29
|
+
👉 [**lucide.dev/icons**](https://lucide.dev/icons)
|
|
30
|
+
|
|
31
|
+
*Nota: Lembre-se que o nome do ícone no site geralmente está em minúsculas com hífen (ex: \`arrow-right\`), mas ao passar na prop \`iconName\` deve usar a convenção do React Native / PascalCase (ex: \`ArrowRight\`).*
|
|
32
|
+
|
|
33
|
+
## O que acontece por baixo do capô? / Como adicionar um novo ícone?
|
|
34
|
+
Para evitar que o motor de compilação (Vite/Metro) tente importar **todos os mais de 1400 ícones** da biblioteca (o que causaria timeouts de memória e bundle enorme), criámos um ficheiro de mapeamento explícito.
|
|
35
|
+
|
|
36
|
+
Se tentar usar um \`iconName\` e ele não aparecer, é porque ainda não foi mapeado!
|
|
37
|
+
Siga este passo-a-passo:
|
|
38
|
+
1. Abra o ficheiro **\`src/utils/iconMap.ts\`**
|
|
39
|
+
2. Importe o novo ícone do \`lucide-react-native\` no topo do ficheiro.
|
|
40
|
+
3. Adicione o ícone ao objeto \`IconMap\`.
|
|
41
|
+
|
|
42
|
+
Pronto! A partir daí, qualquer developer pode referenciar esse ícone através de \`iconName="SeuNovoIcone"\`.
|