@laje/design-system 1.0.0
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/README.md +218 -0
- package/dist/components/Badge.d.ts +9 -0
- package/dist/components/Badge.js +33 -0
- package/dist/components/Button.d.ts +11 -0
- package/dist/components/Button.js +64 -0
- package/dist/components/Card.d.ts +30 -0
- package/dist/components/Card.js +51 -0
- package/dist/components/FormControls.d.ts +25 -0
- package/dist/components/FormControls.js +68 -0
- package/dist/components/Input.d.ts +16 -0
- package/dist/components/Input.js +81 -0
- package/dist/components/Nav.d.ts +39 -0
- package/dist/components/Nav.js +104 -0
- package/dist/components/Stat.d.ts +8 -0
- package/dist/components/Stat.js +16 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +13 -0
- package/dist/tokens/index.d.ts +91 -0
- package/dist/tokens/index.js +157 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# @laje/design-system
|
|
2
|
+
|
|
3
|
+
Design System oficial da **LAJE** — tokens, componentes React e CSS custom properties.
|
|
4
|
+
Extraído diretamente do Figma: [Design System LAJE](https://www.figma.com/design/2hgeRrANocH98PpwwXldg7)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Instalação
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @laje/design-system
|
|
12
|
+
# ou
|
|
13
|
+
yarn add @laje/design-system
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
Importe o CSS global **uma única vez** no root do projeto (ex: `main.tsx` ou `_app.tsx`):
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import '@laje/design-system/styles';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Tokens
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { colors, spacing, radius, typography } from '@laje/design-system/tokens';
|
|
32
|
+
|
|
33
|
+
// Cores
|
|
34
|
+
colors.brand.primary // #6600FF
|
|
35
|
+
colors.brand.secondary // #0ACCE8
|
|
36
|
+
colors.brand.accentPink // #F060A8
|
|
37
|
+
colors.brand.accentOrange // #E02A0A
|
|
38
|
+
colors.purple[500] // #6600FF
|
|
39
|
+
colors.cyan[500] // #0ACCE8
|
|
40
|
+
colors.semantic.success // #21C45E
|
|
41
|
+
|
|
42
|
+
// Spacing
|
|
43
|
+
spacing[16] // "16px"
|
|
44
|
+
spacing[32] // "32px"
|
|
45
|
+
|
|
46
|
+
// Border radius
|
|
47
|
+
radius.md // "8px"
|
|
48
|
+
radius.full // "999px"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Componentes
|
|
54
|
+
|
|
55
|
+
### Button
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { Button } from '@laje/design-system';
|
|
59
|
+
|
|
60
|
+
// Variantes: primary | secondary | dark | outline-orange | ghost
|
|
61
|
+
// Tamanhos: sm | md | lg
|
|
62
|
+
|
|
63
|
+
<Button variant="primary">Começar agora →</Button>
|
|
64
|
+
<Button variant="secondary">Saber mais →</Button>
|
|
65
|
+
<Button variant="dark">Saiba mais e assine</Button>
|
|
66
|
+
<Button variant="outline-orange">Saber mais →</Button>
|
|
67
|
+
<Button variant="ghost">Falar com especialista</Button>
|
|
68
|
+
<Button variant="primary" loading>Carregando...</Button>
|
|
69
|
+
<Button variant="primary" disabled>Indisponível</Button>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Badge
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { Badge } from '@laje/design-system';
|
|
76
|
+
|
|
77
|
+
// Variantes: trilha | popular | growth | design | novo | custom
|
|
78
|
+
<Badge variant="trilha">Trilha</Badge>
|
|
79
|
+
<Badge variant="popular">MAIS POPULAR</Badge>
|
|
80
|
+
<Badge variant="growth">Growth</Badge>
|
|
81
|
+
<Badge variant="design">Design</Badge>
|
|
82
|
+
<Badge variant="novo">Novo</Badge>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Input
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { Input, Select } from '@laje/design-system';
|
|
89
|
+
|
|
90
|
+
<Input label="Nome completo" placeholder="Digite seu nome..." />
|
|
91
|
+
<Input label="E-mail" type="email" placeholder="nome@laje.com" />
|
|
92
|
+
<Input label="Senha" type="password" error="Mínimo 8 caracteres" />
|
|
93
|
+
<Input variant="search" placeholder="Buscar cursos, trilhas ou certificados..." />
|
|
94
|
+
|
|
95
|
+
<Select
|
|
96
|
+
label="Selecione uma trilha"
|
|
97
|
+
options={[
|
|
98
|
+
{ value: 'branding', label: 'Branding' },
|
|
99
|
+
{ value: 'growth', label: 'Growth' },
|
|
100
|
+
{ value: 'design', label: 'Design' },
|
|
101
|
+
{ value: 'experiencia', label: 'Experiência' },
|
|
102
|
+
{ value: 'comunicacao', label: 'Comunicação' },
|
|
103
|
+
]}
|
|
104
|
+
/>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Cards
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { CardFeature, CardTrail, CardPricing } from '@laje/design-system';
|
|
111
|
+
|
|
112
|
+
<CardFeature
|
|
113
|
+
icon="👥"
|
|
114
|
+
title="Comunidade Ativa"
|
|
115
|
+
description="Conecte-se com +5.000 profissionais de branding, marketing e inovação."
|
|
116
|
+
/>
|
|
117
|
+
|
|
118
|
+
<CardTrail
|
|
119
|
+
badge="growth"
|
|
120
|
+
badgeLabel="Growth"
|
|
121
|
+
title="Growth"
|
|
122
|
+
subtitle="Marketing de Crescimento"
|
|
123
|
+
onClick={() => navigate('/trilhas/growth')}
|
|
124
|
+
/>
|
|
125
|
+
|
|
126
|
+
<CardPricing
|
|
127
|
+
featured
|
|
128
|
+
ctaLabel="Começar agora →"
|
|
129
|
+
onCtaClick={() => navigate('/checkout')}
|
|
130
|
+
/>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Form Controls
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { Checkbox, Radio, Toggle } from '@laje/design-system';
|
|
137
|
+
|
|
138
|
+
<Checkbox label="Aceito os termos de uso" checked={accepted} onChange={setAccepted} />
|
|
139
|
+
<Radio label="Anual" value="anual" checked={plan === 'anual'} onChange={setPlan} />
|
|
140
|
+
<Toggle label="Notificações push" checked={notif} onChange={setNotif} />
|
|
141
|
+
<Toggle label="Modo escuro" checked={dark} color="cyan" onChange={setDark} />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Navegação
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { Header, Tabs, Sidebar, Breadcrumb } from '@laje/design-system';
|
|
148
|
+
|
|
149
|
+
<Header onCtaClick={() => navigate('/assinar')} />
|
|
150
|
+
|
|
151
|
+
<Tabs
|
|
152
|
+
tabs={['Visão Geral', 'Trilhas', 'Certificados', 'Comunidade', 'Vagas']}
|
|
153
|
+
theme="dark"
|
|
154
|
+
onChange={(tab) => console.log(tab)}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<Breadcrumb items={[
|
|
158
|
+
{ label: 'Home', href: '/' },
|
|
159
|
+
{ label: 'Trilhas', href: '/trilhas' },
|
|
160
|
+
{ label: 'Branding' },
|
|
161
|
+
]} />
|
|
162
|
+
|
|
163
|
+
<Sidebar user={{ name: 'James Pereira', role: 'Assinante LAJE' }} />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Stat
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { Stat } from '@laje/design-system';
|
|
170
|
+
|
|
171
|
+
<Stat value="+5.000" label="Membros ativos" />
|
|
172
|
+
<Stat value="Semanal" label="Encontros ao vivo" />
|
|
173
|
+
<Stat value="100+" label="Vagas exclusivas/mês" />
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Figma Make Kit
|
|
179
|
+
|
|
180
|
+
Para usar no **Figma Make**:
|
|
181
|
+
|
|
182
|
+
1. Abra o Figma Make (`figma.com/make`)
|
|
183
|
+
2. Clique em **"Create a kit"** → **"npm packages"**
|
|
184
|
+
3. Instale: `@laje/design-system`
|
|
185
|
+
4. O Make vai reconhecer os tokens e componentes da LAJE automaticamente
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Tailwind Config (opcional)
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
// tailwind.config.js
|
|
193
|
+
const { colors, spacing, radius } = require('@laje/design-system/tokens');
|
|
194
|
+
|
|
195
|
+
module.exports = {
|
|
196
|
+
theme: {
|
|
197
|
+
extend: {
|
|
198
|
+
colors: {
|
|
199
|
+
laje: colors,
|
|
200
|
+
},
|
|
201
|
+
spacing: Object.fromEntries(
|
|
202
|
+
Object.entries(spacing).map(([k, v]) => [k, v])
|
|
203
|
+
),
|
|
204
|
+
borderRadius: {
|
|
205
|
+
'laje-sm': radius.sm,
|
|
206
|
+
'laje-md': radius.md,
|
|
207
|
+
'laje-lg': radius.lg,
|
|
208
|
+
'laje-xl': radius.xl,
|
|
209
|
+
'laje-full': radius.full,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
Fonte: [Figma Design System LAJE](https://www.figma.com/design/2hgeRrANocH98PpwwXldg7)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type BadgeVariant = "trilha" | "popular" | "growth" | "design" | "novo" | "custom";
|
|
3
|
+
export interface BadgeProps {
|
|
4
|
+
variant?: BadgeVariant;
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
style?: React.CSSProperties;
|
|
7
|
+
}
|
|
8
|
+
export declare const Badge: React.FC<BadgeProps>;
|
|
9
|
+
export default Badge;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { colors, radius, spacing, typography } from "../tokens";
|
|
3
|
+
const variantMap = {
|
|
4
|
+
trilha: {
|
|
5
|
+
background: colors.purple[500],
|
|
6
|
+
color: colors.neutral.white,
|
|
7
|
+
},
|
|
8
|
+
popular: {
|
|
9
|
+
background: colors.brand.accentPink,
|
|
10
|
+
color: colors.neutral.white,
|
|
11
|
+
},
|
|
12
|
+
growth: {
|
|
13
|
+
background: colors.brand.secondary,
|
|
14
|
+
color: colors.neutral.black,
|
|
15
|
+
},
|
|
16
|
+
design: {
|
|
17
|
+
background: colors.cyan[300],
|
|
18
|
+
color: colors.neutral.black,
|
|
19
|
+
},
|
|
20
|
+
novo: {
|
|
21
|
+
background: colors.brand.primary,
|
|
22
|
+
color: colors.neutral.white,
|
|
23
|
+
},
|
|
24
|
+
custom: {
|
|
25
|
+
background: colors.purple[100],
|
|
26
|
+
color: colors.purple[600],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export const Badge = ({ variant = "trilha", children, style, }) => {
|
|
30
|
+
const baseStyle = Object.assign(Object.assign({ display: "inline-flex", alignItems: "center", padding: `${spacing[4]} ${spacing[12]}`, borderRadius: radius.full, fontSize: typography.fontSize.xs, fontWeight: typography.fontWeight.semibold, fontFamily: typography.fontFamily.sans, letterSpacing: "0.04em", textTransform: "uppercase", whiteSpace: "nowrap" }, variantMap[variant]), style);
|
|
31
|
+
return React.createElement("span", { style: baseStyle }, children);
|
|
32
|
+
};
|
|
33
|
+
export default Badge;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type ButtonVariant = "primary" | "secondary" | "dark" | "outline-orange" | "ghost";
|
|
3
|
+
export type ButtonSize = "sm" | "md" | "lg";
|
|
4
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
variant?: ButtonVariant;
|
|
6
|
+
size?: ButtonSize;
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
export declare const Button: React.FC<ButtonProps>;
|
|
11
|
+
export default Button;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React from "react";
|
|
13
|
+
import { colors, radius, spacing, typography } from "../tokens";
|
|
14
|
+
const variantStyles = {
|
|
15
|
+
primary: {
|
|
16
|
+
background: colors.brand.primary,
|
|
17
|
+
color: colors.neutral.white,
|
|
18
|
+
border: "none",
|
|
19
|
+
},
|
|
20
|
+
secondary: {
|
|
21
|
+
background: colors.brand.secondary,
|
|
22
|
+
color: colors.neutral.black,
|
|
23
|
+
border: "none",
|
|
24
|
+
},
|
|
25
|
+
dark: {
|
|
26
|
+
background: colors.neutral.black,
|
|
27
|
+
color: colors.neutral.white,
|
|
28
|
+
border: `1.5px solid ${colors.purple[600]}`,
|
|
29
|
+
},
|
|
30
|
+
"outline-orange": {
|
|
31
|
+
background: "transparent",
|
|
32
|
+
color: colors.brand.accentOrange,
|
|
33
|
+
border: `1.5px solid ${colors.brand.accentOrange}`,
|
|
34
|
+
},
|
|
35
|
+
ghost: {
|
|
36
|
+
background: "transparent",
|
|
37
|
+
color: colors.neutral.white,
|
|
38
|
+
border: `1.5px solid rgba(240,235,223,0.3)`,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const sizeStyles = {
|
|
42
|
+
sm: {
|
|
43
|
+
padding: `${spacing[8]} ${spacing[16]}`,
|
|
44
|
+
fontSize: typography.fontSize.base,
|
|
45
|
+
height: "36px",
|
|
46
|
+
},
|
|
47
|
+
md: {
|
|
48
|
+
padding: `${spacing[12]} ${spacing[24]}`,
|
|
49
|
+
fontSize: typography.fontSize.md,
|
|
50
|
+
height: "48px",
|
|
51
|
+
},
|
|
52
|
+
lg: {
|
|
53
|
+
padding: `${spacing[16]} ${spacing[32]}`,
|
|
54
|
+
fontSize: typography.fontSize.lg,
|
|
55
|
+
height: "56px",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export const Button = (_a) => {
|
|
59
|
+
var { variant = "primary", size = "md", loading = false, disabled, children, style } = _a, rest = __rest(_a, ["variant", "size", "loading", "disabled", "children", "style"]);
|
|
60
|
+
const isDisabled = disabled || loading;
|
|
61
|
+
const baseStyle = Object.assign(Object.assign(Object.assign({ display: "inline-flex", alignItems: "center", justifyContent: "center", gap: spacing[8], borderRadius: radius.md, fontFamily: typography.fontFamily.sans, fontWeight: typography.fontWeight.semibold, cursor: isDisabled ? "not-allowed" : "pointer", opacity: isDisabled ? 0.45 : 1, transition: "opacity 150ms ease, transform 100ms ease, box-shadow 150ms ease", whiteSpace: "nowrap" }, variantStyles[variant]), sizeStyles[size]), style);
|
|
62
|
+
return (React.createElement("button", Object.assign({ style: baseStyle, disabled: isDisabled }, rest), loading ? "Carregando..." : children));
|
|
63
|
+
};
|
|
64
|
+
export default Button;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BadgeVariant } from "./Badge";
|
|
3
|
+
export interface CardFeatureProps {
|
|
4
|
+
icon?: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description: string;
|
|
7
|
+
style?: React.CSSProperties;
|
|
8
|
+
}
|
|
9
|
+
export declare const CardFeature: React.FC<CardFeatureProps>;
|
|
10
|
+
export interface CardTrailProps {
|
|
11
|
+
badge: BadgeVariant;
|
|
12
|
+
badgeLabel: string;
|
|
13
|
+
title: string;
|
|
14
|
+
subtitle: string;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
style?: React.CSSProperties;
|
|
17
|
+
}
|
|
18
|
+
export declare const CardTrail: React.FC<CardTrailProps>;
|
|
19
|
+
export interface CardPricingProps {
|
|
20
|
+
title?: string;
|
|
21
|
+
subtitle?: string;
|
|
22
|
+
priceMonthly?: string;
|
|
23
|
+
priceAnnual?: string;
|
|
24
|
+
features?: string[];
|
|
25
|
+
ctaLabel?: string;
|
|
26
|
+
onCtaClick?: () => void;
|
|
27
|
+
featured?: boolean;
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
}
|
|
30
|
+
export declare const CardPricing: React.FC<CardPricingProps>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { colors, radius, spacing, typography } from "../tokens";
|
|
3
|
+
import { Badge } from "./Badge";
|
|
4
|
+
import { Button } from "./Button";
|
|
5
|
+
export const CardFeature = ({ icon = "◎", title, description, style, }) => {
|
|
6
|
+
const cardStyle = Object.assign({ background: "rgba(240,235,223,0.05)", border: "1px solid rgba(240,235,223,0.1)", borderRadius: radius.lg, padding: spacing[24], display: "flex", flexDirection: "column", gap: spacing[12] }, style);
|
|
7
|
+
return (React.createElement("div", { style: cardStyle },
|
|
8
|
+
React.createElement("div", { style: {
|
|
9
|
+
width: "40px",
|
|
10
|
+
height: "40px",
|
|
11
|
+
borderRadius: radius.full,
|
|
12
|
+
background: `linear-gradient(135deg, ${colors.brand.primary}, ${colors.brand.secondary})`,
|
|
13
|
+
display: "flex",
|
|
14
|
+
alignItems: "center",
|
|
15
|
+
justifyContent: "center",
|
|
16
|
+
fontSize: "18px",
|
|
17
|
+
} }, icon),
|
|
18
|
+
React.createElement("div", { style: { fontSize: typography.fontSize.lg, fontWeight: typography.fontWeight.bold, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, title),
|
|
19
|
+
React.createElement("div", { style: { fontSize: typography.fontSize.md, color: "rgba(240,235,223,0.6)", fontFamily: typography.fontFamily.sans, lineHeight: 1.6 } }, description)));
|
|
20
|
+
};
|
|
21
|
+
export const CardTrail = ({ badge, badgeLabel, title, subtitle, onClick, style, }) => {
|
|
22
|
+
return (React.createElement("div", { onClick: onClick, style: Object.assign({ background: "rgba(240,235,223,0.05)", border: "1px solid rgba(240,235,223,0.1)", borderRadius: radius.lg, padding: spacing[24], display: "flex", alignItems: "center", justifyContent: "space-between", cursor: onClick ? "pointer" : "default", transition: "background 150ms ease, border-color 150ms ease" }, style) },
|
|
23
|
+
React.createElement("div", { style: { display: "flex", flexDirection: "column", gap: spacing[8] } },
|
|
24
|
+
React.createElement(Badge, { variant: badge }, badgeLabel),
|
|
25
|
+
React.createElement("div", { style: { fontSize: typography.fontSize.xl, fontWeight: typography.fontWeight.bold, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, title),
|
|
26
|
+
React.createElement("div", { style: { fontSize: typography.fontSize.base, color: "rgba(240,235,223,0.5)", fontFamily: typography.fontFamily.sans } }, subtitle)),
|
|
27
|
+
React.createElement("span", { style: { fontSize: "20px", color: "rgba(240,235,223,0.4)" } }, "\u2228")));
|
|
28
|
+
};
|
|
29
|
+
export const CardPricing = ({ title = "Assinatura LAJE", subtitle = "Aprenda no seu ritmo com acesso total ao ecossistema", priceMonthly = "12x R$ 49,99", priceAnnual = "ou R$ 599,88/ano", features = [
|
|
30
|
+
"+100h de cursos de Branding, Marketing e Inovação",
|
|
31
|
+
"Encontros ao vivo semanais",
|
|
32
|
+
"Comunidade exclusiva e networking",
|
|
33
|
+
"Certificados reconhecidos pelo mercado",
|
|
34
|
+
"App exclusivo para mobile",
|
|
35
|
+
], ctaLabel = "Começar agora →", onCtaClick, featured = true, style, }) => {
|
|
36
|
+
return (React.createElement("div", { style: Object.assign({ background: featured
|
|
37
|
+
? `linear-gradient(135deg, ${colors.purple[600]}, ${colors.neutral.black})`
|
|
38
|
+
: "rgba(240,235,223,0.05)", border: `1.5px solid ${featured ? colors.brand.primary : "rgba(240,235,223,0.1)"}`, borderRadius: radius.xl, padding: spacing[32], display: "flex", flexDirection: "column", gap: spacing[24] }, style) },
|
|
39
|
+
featured && (React.createElement(Badge, { variant: "popular" }, "MAIS POPULAR")),
|
|
40
|
+
React.createElement("div", null,
|
|
41
|
+
React.createElement("div", { style: { fontSize: typography.fontSize["2xl"], fontWeight: typography.fontWeight.bold, color: colors.neutral.white, fontFamily: typography.fontFamily.sans, marginBottom: spacing[8] } }, title),
|
|
42
|
+
React.createElement("div", { style: { fontSize: typography.fontSize.md, color: "rgba(240,235,223,0.6)", fontFamily: typography.fontFamily.sans } }, subtitle)),
|
|
43
|
+
React.createElement("div", null,
|
|
44
|
+
React.createElement("div", { style: { fontSize: typography.fontSize["4xl"], fontWeight: typography.fontWeight.bold, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, priceMonthly),
|
|
45
|
+
React.createElement("div", { style: { fontSize: typography.fontSize.base, color: "rgba(240,235,223,0.5)", fontFamily: typography.fontFamily.sans, marginTop: spacing[4] } }, priceAnnual)),
|
|
46
|
+
React.createElement("ul", { style: { listStyle: "none", display: "flex", flexDirection: "column", gap: spacing[12] } }, features.map((f, i) => (React.createElement("li", { key: i, style: { fontSize: typography.fontSize.md, color: "rgba(240,235,223,0.8)", fontFamily: typography.fontFamily.sans, display: "flex", gap: spacing[8] } },
|
|
47
|
+
React.createElement("span", { style: { color: colors.brand.secondary } }, "\u2022"),
|
|
48
|
+
" ",
|
|
49
|
+
f)))),
|
|
50
|
+
React.createElement(Button, { variant: "primary", onClick: onCtaClick, style: { width: "100%" } }, ctaLabel)));
|
|
51
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface CheckboxProps {
|
|
3
|
+
label: string;
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
onChange?: (checked: boolean) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const Checkbox: React.FC<CheckboxProps>;
|
|
9
|
+
export interface RadioProps {
|
|
10
|
+
label: string;
|
|
11
|
+
checked?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
name?: string;
|
|
14
|
+
value?: string;
|
|
15
|
+
onChange?: (value: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const Radio: React.FC<RadioProps>;
|
|
18
|
+
export interface ToggleProps {
|
|
19
|
+
label?: string;
|
|
20
|
+
checked?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
color?: "purple" | "cyan";
|
|
23
|
+
onChange?: (checked: boolean) => void;
|
|
24
|
+
}
|
|
25
|
+
export declare const Toggle: React.FC<ToggleProps>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { colors, radius, spacing, typography } from "../tokens";
|
|
3
|
+
export const Checkbox = ({ label, checked = false, disabled, onChange }) => {
|
|
4
|
+
const boxStyle = {
|
|
5
|
+
width: "20px",
|
|
6
|
+
height: "20px",
|
|
7
|
+
borderRadius: radius.sm,
|
|
8
|
+
border: `2px solid ${checked ? colors.brand.primary : "rgba(240,235,223,0.3)"}`,
|
|
9
|
+
background: checked ? colors.brand.primary : "transparent",
|
|
10
|
+
display: "flex",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
justifyContent: "center",
|
|
13
|
+
flexShrink: 0,
|
|
14
|
+
transition: "all 150ms ease",
|
|
15
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
16
|
+
opacity: disabled ? 0.4 : 1,
|
|
17
|
+
};
|
|
18
|
+
return (React.createElement("div", { style: { display: "flex", alignItems: "center", gap: spacing[12], cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.4 : 1 }, onClick: () => !disabled && (onChange === null || onChange === void 0 ? void 0 : onChange(!checked)) },
|
|
19
|
+
React.createElement("div", { style: boxStyle }, checked && React.createElement("span", { style: { color: colors.neutral.white, fontSize: "12px", lineHeight: 1 } }, "\u2713")),
|
|
20
|
+
React.createElement("span", { style: { fontSize: typography.fontSize.md, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, label)));
|
|
21
|
+
};
|
|
22
|
+
export const Radio = ({ label, checked = false, disabled, value = "", onChange }) => {
|
|
23
|
+
return (React.createElement("div", { style: { display: "flex", alignItems: "center", gap: spacing[12], cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.4 : 1 }, onClick: () => !disabled && (onChange === null || onChange === void 0 ? void 0 : onChange(value)) },
|
|
24
|
+
React.createElement("div", { style: {
|
|
25
|
+
width: "20px",
|
|
26
|
+
height: "20px",
|
|
27
|
+
borderRadius: radius.full,
|
|
28
|
+
border: `2px solid ${checked ? colors.brand.primary : "rgba(240,235,223,0.3)"}`,
|
|
29
|
+
display: "flex",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
justifyContent: "center",
|
|
32
|
+
flexShrink: 0,
|
|
33
|
+
transition: "all 150ms ease",
|
|
34
|
+
} }, checked && (React.createElement("div", { style: {
|
|
35
|
+
width: "10px",
|
|
36
|
+
height: "10px",
|
|
37
|
+
borderRadius: radius.full,
|
|
38
|
+
background: colors.brand.primary,
|
|
39
|
+
} }))),
|
|
40
|
+
React.createElement("span", { style: { fontSize: typography.fontSize.md, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, label)));
|
|
41
|
+
};
|
|
42
|
+
export const Toggle = ({ label, checked = false, disabled, color = "purple", onChange, }) => {
|
|
43
|
+
const trackColor = checked
|
|
44
|
+
? (color === "cyan" ? colors.brand.secondary : colors.brand.primary)
|
|
45
|
+
: "rgba(240,235,223,0.15)";
|
|
46
|
+
return (React.createElement("div", { style: { display: "flex", alignItems: "center", gap: spacing[12], cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.4 : 1 }, onClick: () => !disabled && (onChange === null || onChange === void 0 ? void 0 : onChange(!checked)) },
|
|
47
|
+
React.createElement("div", { style: {
|
|
48
|
+
width: "44px",
|
|
49
|
+
height: "24px",
|
|
50
|
+
borderRadius: radius.full,
|
|
51
|
+
background: trackColor,
|
|
52
|
+
position: "relative",
|
|
53
|
+
transition: "background 200ms ease",
|
|
54
|
+
flexShrink: 0,
|
|
55
|
+
} },
|
|
56
|
+
React.createElement("div", { style: {
|
|
57
|
+
width: "18px",
|
|
58
|
+
height: "18px",
|
|
59
|
+
borderRadius: radius.full,
|
|
60
|
+
background: colors.neutral.white,
|
|
61
|
+
position: "absolute",
|
|
62
|
+
top: "3px",
|
|
63
|
+
left: checked ? "23px" : "3px",
|
|
64
|
+
transition: "left 200ms ease",
|
|
65
|
+
boxShadow: "0 1px 4px rgba(0,0,0,0.3)",
|
|
66
|
+
} })),
|
|
67
|
+
label && (React.createElement("span", { style: { fontSize: typography.fontSize.md, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, label))));
|
|
68
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
3
|
+
label?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
variant?: "default" | "search";
|
|
6
|
+
}
|
|
7
|
+
export declare const Input: React.FC<InputProps>;
|
|
8
|
+
export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
|
9
|
+
label?: string;
|
|
10
|
+
options: {
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}[];
|
|
14
|
+
}
|
|
15
|
+
export declare const Select: React.FC<SelectProps>;
|
|
16
|
+
export default Input;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { useState } from "react";
|
|
13
|
+
import { colors, radius, spacing, typography } from "../tokens";
|
|
14
|
+
export const Input = (_a) => {
|
|
15
|
+
var { label, error, variant = "default", disabled, style } = _a, rest = __rest(_a, ["label", "error", "variant", "disabled", "style"]);
|
|
16
|
+
const [focused, setFocused] = useState(false);
|
|
17
|
+
const borderColor = error
|
|
18
|
+
? colors.semantic.error
|
|
19
|
+
: focused
|
|
20
|
+
? colors.brand.primary
|
|
21
|
+
: "rgba(240,235,223,0.2)";
|
|
22
|
+
const wrapperStyle = {
|
|
23
|
+
display: "flex",
|
|
24
|
+
flexDirection: "column",
|
|
25
|
+
gap: spacing[8],
|
|
26
|
+
width: "100%",
|
|
27
|
+
};
|
|
28
|
+
const labelStyle = {
|
|
29
|
+
fontSize: typography.fontSize.sm,
|
|
30
|
+
fontWeight: typography.fontWeight.medium,
|
|
31
|
+
fontFamily: typography.fontFamily.sans,
|
|
32
|
+
color: "rgba(240,235,223,0.6)",
|
|
33
|
+
};
|
|
34
|
+
const inputStyle = Object.assign({ width: "100%", height: "52px", padding: `0 ${spacing[16]}`, paddingLeft: variant === "search" ? spacing[40] : spacing[16], background: "rgba(15,11,26,0.6)", border: `1.5px solid ${borderColor}`, borderRadius: radius.md, color: colors.neutral.white, fontSize: typography.fontSize.md, fontFamily: typography.fontFamily.sans, outline: "none", opacity: disabled ? 0.4 : 1, cursor: disabled ? "not-allowed" : "text", transition: "border-color 150ms ease" }, style);
|
|
35
|
+
const errorStyle = {
|
|
36
|
+
fontSize: typography.fontSize.xs,
|
|
37
|
+
color: colors.semantic.error,
|
|
38
|
+
fontFamily: typography.fontFamily.sans,
|
|
39
|
+
};
|
|
40
|
+
return (React.createElement("div", { style: wrapperStyle },
|
|
41
|
+
label && React.createElement("label", { style: labelStyle }, label),
|
|
42
|
+
React.createElement("div", { style: { position: "relative", width: "100%" } },
|
|
43
|
+
variant === "search" && (React.createElement("span", { style: {
|
|
44
|
+
position: "absolute",
|
|
45
|
+
left: spacing[16],
|
|
46
|
+
top: "50%",
|
|
47
|
+
transform: "translateY(-50%)",
|
|
48
|
+
color: "rgba(240,235,223,0.4)",
|
|
49
|
+
fontSize: "18px",
|
|
50
|
+
pointerEvents: "none",
|
|
51
|
+
} }, "\u2315")),
|
|
52
|
+
React.createElement("input", Object.assign({ style: inputStyle, disabled: disabled, onFocus: () => setFocused(true), onBlur: () => setFocused(false) }, rest))),
|
|
53
|
+
error && React.createElement("span", { style: errorStyle },
|
|
54
|
+
"\u26A0 ",
|
|
55
|
+
error)));
|
|
56
|
+
};
|
|
57
|
+
export const Select = (_a) => {
|
|
58
|
+
var { label, options, disabled, style } = _a, rest = __rest(_a, ["label", "options", "disabled", "style"]);
|
|
59
|
+
const [focused, setFocused] = useState(false);
|
|
60
|
+
const wrapperStyle = { display: "flex", flexDirection: "column", gap: spacing[8], width: "100%" };
|
|
61
|
+
const labelStyle = {
|
|
62
|
+
fontSize: typography.fontSize.sm,
|
|
63
|
+
fontWeight: typography.fontWeight.medium,
|
|
64
|
+
fontFamily: typography.fontFamily.sans,
|
|
65
|
+
color: "rgba(240,235,223,0.6)",
|
|
66
|
+
};
|
|
67
|
+
const selectStyle = Object.assign({ width: "100%", height: "52px", padding: `0 ${spacing[40]} 0 ${spacing[16]}`, background: "rgba(15,11,26,0.6)", border: `1.5px solid ${focused ? colors.brand.primary : "rgba(240,235,223,0.2)"}`, borderRadius: radius.md, color: colors.neutral.white, fontSize: typography.fontSize.md, fontFamily: typography.fontFamily.sans, appearance: "none", outline: "none", cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.4 : 1, transition: "border-color 150ms ease" }, style);
|
|
68
|
+
return (React.createElement("div", { style: wrapperStyle },
|
|
69
|
+
label && React.createElement("label", { style: labelStyle }, label),
|
|
70
|
+
React.createElement("div", { style: { position: "relative", width: "100%" } },
|
|
71
|
+
React.createElement("select", Object.assign({ style: selectStyle, disabled: disabled, onFocus: () => setFocused(true), onBlur: () => setFocused(false) }, rest), options.map(opt => (React.createElement("option", { key: opt.value, value: opt.value }, opt.label)))),
|
|
72
|
+
React.createElement("span", { style: {
|
|
73
|
+
position: "absolute",
|
|
74
|
+
right: spacing[16],
|
|
75
|
+
top: "50%",
|
|
76
|
+
transform: "translateY(-50%)",
|
|
77
|
+
color: "rgba(240,235,223,0.5)",
|
|
78
|
+
pointerEvents: "none",
|
|
79
|
+
} }, "\u2228"))));
|
|
80
|
+
};
|
|
81
|
+
export default Input;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface NavItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface HeaderProps {
|
|
7
|
+
items?: NavItem[];
|
|
8
|
+
ctaLabel?: string;
|
|
9
|
+
onCtaClick?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare const Header: React.FC<HeaderProps>;
|
|
12
|
+
export interface TabsProps {
|
|
13
|
+
tabs: string[];
|
|
14
|
+
activeTab?: string;
|
|
15
|
+
theme?: "dark" | "light";
|
|
16
|
+
onChange?: (tab: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare const Tabs: React.FC<TabsProps>;
|
|
19
|
+
export interface BreadcrumbProps {
|
|
20
|
+
items: {
|
|
21
|
+
label: string;
|
|
22
|
+
href?: string;
|
|
23
|
+
}[];
|
|
24
|
+
}
|
|
25
|
+
export declare const Breadcrumb: React.FC<BreadcrumbProps>;
|
|
26
|
+
export interface SidebarItem {
|
|
27
|
+
label: string;
|
|
28
|
+
icon?: string;
|
|
29
|
+
href?: string;
|
|
30
|
+
active?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface SidebarProps {
|
|
33
|
+
items?: SidebarItem[];
|
|
34
|
+
user?: {
|
|
35
|
+
name: string;
|
|
36
|
+
role: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare const Sidebar: React.FC<SidebarProps>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { colors, spacing, typography } from "../tokens";
|
|
3
|
+
import { Button } from "./Button";
|
|
4
|
+
export const Header = ({ items = [
|
|
5
|
+
{ label: "Trilhas" },
|
|
6
|
+
{ label: "Cursos" },
|
|
7
|
+
{ label: "Comunidade" },
|
|
8
|
+
{ label: "Vagas" },
|
|
9
|
+
{ label: "Eventos" },
|
|
10
|
+
], ctaLabel = "Assinar →", onCtaClick, }) => (React.createElement("header", { style: {
|
|
11
|
+
display: "flex",
|
|
12
|
+
alignItems: "center",
|
|
13
|
+
justifyContent: "space-between",
|
|
14
|
+
padding: `0 ${spacing[80]}`,
|
|
15
|
+
height: "72px",
|
|
16
|
+
background: colors.neutral.black,
|
|
17
|
+
borderBottom: "1px solid rgba(240,235,223,0.08)",
|
|
18
|
+
position: "sticky",
|
|
19
|
+
top: 0,
|
|
20
|
+
zIndex: 100,
|
|
21
|
+
} },
|
|
22
|
+
React.createElement("span", { style: { fontSize: typography.fontSize.xl, fontWeight: typography.fontWeight.bold, color: colors.neutral.white, fontFamily: typography.fontFamily.sans, letterSpacing: "-0.02em" } }, "LA_JE"),
|
|
23
|
+
React.createElement("nav", { style: { display: "flex", gap: spacing[32] } }, items.map(item => (React.createElement("a", { key: item.label, href: item.href || "#", style: {
|
|
24
|
+
fontSize: typography.fontSize.base,
|
|
25
|
+
color: "rgba(240,235,223,0.6)",
|
|
26
|
+
fontFamily: typography.fontFamily.sans,
|
|
27
|
+
textDecoration: "none",
|
|
28
|
+
transition: "color 150ms ease",
|
|
29
|
+
} }, item.label)))),
|
|
30
|
+
React.createElement(Button, { variant: "primary", size: "sm", onClick: onCtaClick }, ctaLabel)));
|
|
31
|
+
export const Tabs = ({ tabs = ["Visão Geral", "Trilhas", "Certificados", "Comunidade", "Vagas"], activeTab, theme = "dark", onChange, }) => {
|
|
32
|
+
const [active, setActive] = useState(activeTab || tabs[0]);
|
|
33
|
+
const isDark = theme === "dark";
|
|
34
|
+
return (React.createElement("div", { style: {
|
|
35
|
+
display: "flex",
|
|
36
|
+
gap: 0,
|
|
37
|
+
borderBottom: `1px solid ${isDark ? "rgba(240,235,223,0.1)" : "rgba(15,11,26,0.1)"}`,
|
|
38
|
+
background: isDark ? colors.neutral.black : colors.neutral.white,
|
|
39
|
+
} }, tabs.map(tab => {
|
|
40
|
+
const isActive = tab === active;
|
|
41
|
+
return (React.createElement("button", { key: tab, onClick: () => { setActive(tab); onChange === null || onChange === void 0 ? void 0 : onChange(tab); }, style: {
|
|
42
|
+
padding: `${spacing[12]} ${spacing[24]}`,
|
|
43
|
+
background: "transparent",
|
|
44
|
+
border: "none",
|
|
45
|
+
borderBottom: `2px solid ${isActive ? colors.brand.primary : "transparent"}`,
|
|
46
|
+
color: isActive
|
|
47
|
+
? (isDark ? colors.neutral.white : colors.neutral.black)
|
|
48
|
+
: (isDark ? "rgba(240,235,223,0.4)" : "rgba(15,11,26,0.4)"),
|
|
49
|
+
fontSize: typography.fontSize.base,
|
|
50
|
+
fontWeight: isActive ? typography.fontWeight.semibold : typography.fontWeight.regular,
|
|
51
|
+
fontFamily: typography.fontFamily.sans,
|
|
52
|
+
cursor: "pointer",
|
|
53
|
+
transition: "all 150ms ease",
|
|
54
|
+
whiteSpace: "nowrap",
|
|
55
|
+
} }, tab));
|
|
56
|
+
})));
|
|
57
|
+
};
|
|
58
|
+
export const Breadcrumb = ({ items }) => (React.createElement("nav", { style: { display: "flex", alignItems: "center", gap: spacing[8] } }, items.map((item, i) => (React.createElement(React.Fragment, { key: i },
|
|
59
|
+
i > 0 && (React.createElement("span", { style: { color: "rgba(240,235,223,0.3)", fontSize: typography.fontSize.sm, fontFamily: typography.fontFamily.sans } }, "/")),
|
|
60
|
+
React.createElement("a", { href: item.href || "#", style: {
|
|
61
|
+
fontSize: typography.fontSize.sm,
|
|
62
|
+
color: i === items.length - 1 ? colors.neutral.white : "rgba(240,235,223,0.4)",
|
|
63
|
+
fontFamily: typography.fontFamily.sans,
|
|
64
|
+
textDecoration: "none",
|
|
65
|
+
fontWeight: i === items.length - 1 ? typography.fontWeight.medium : typography.fontWeight.regular,
|
|
66
|
+
} }, item.label))))));
|
|
67
|
+
export const Sidebar = ({ items = [
|
|
68
|
+
{ label: "Dashboard", icon: "⊞", active: true },
|
|
69
|
+
{ label: "Trilhas", icon: "◈" },
|
|
70
|
+
{ label: "Meus Cursos", icon: "▶" },
|
|
71
|
+
{ label: "Certificados", icon: "◎" },
|
|
72
|
+
{ label: "Comunidade", icon: "◉" },
|
|
73
|
+
{ label: "Painel de Vagas", icon: "◇" },
|
|
74
|
+
{ label: "Eventos ao Vivo", icon: "◈" },
|
|
75
|
+
], user = { name: "James Pereira", role: "Assinante LAJE" }, }) => (React.createElement("aside", { style: {
|
|
76
|
+
width: "240px",
|
|
77
|
+
height: "100vh",
|
|
78
|
+
background: colors.neutral.black,
|
|
79
|
+
borderRight: "1px solid rgba(240,235,223,0.08)",
|
|
80
|
+
display: "flex",
|
|
81
|
+
flexDirection: "column",
|
|
82
|
+
padding: `${spacing[32]} 0`,
|
|
83
|
+
position: "sticky",
|
|
84
|
+
top: 0,
|
|
85
|
+
} },
|
|
86
|
+
React.createElement("span", { style: { fontSize: typography.fontSize.xl, fontWeight: typography.fontWeight.bold, color: colors.neutral.white, fontFamily: typography.fontFamily.sans, padding: `0 ${spacing[24]}`, marginBottom: spacing[32] } }, "LA_JE"),
|
|
87
|
+
React.createElement("nav", { style: { display: "flex", flexDirection: "column", flex: 1 } }, items.map(item => (React.createElement("a", { key: item.label, href: item.href || "#", style: {
|
|
88
|
+
display: "flex",
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
gap: spacing[12],
|
|
91
|
+
padding: `${spacing[12]} ${spacing[24]}`,
|
|
92
|
+
color: item.active ? colors.neutral.white : "rgba(240,235,223,0.45)",
|
|
93
|
+
background: item.active ? "rgba(102,0,255,0.15)" : "transparent",
|
|
94
|
+
borderLeft: `3px solid ${item.active ? colors.brand.primary : "transparent"}`,
|
|
95
|
+
fontSize: typography.fontSize.base,
|
|
96
|
+
fontFamily: typography.fontFamily.sans,
|
|
97
|
+
textDecoration: "none",
|
|
98
|
+
transition: "all 150ms ease",
|
|
99
|
+
} },
|
|
100
|
+
React.createElement("span", null, item.icon),
|
|
101
|
+
item.label)))),
|
|
102
|
+
React.createElement("div", { style: { padding: `${spacing[16]} ${spacing[24]}`, borderTop: "1px solid rgba(240,235,223,0.08)", display: "flex", flexDirection: "column", gap: spacing[4] } },
|
|
103
|
+
React.createElement("span", { style: { fontSize: typography.fontSize.base, fontWeight: typography.fontWeight.medium, color: colors.neutral.white, fontFamily: typography.fontFamily.sans } }, user.name),
|
|
104
|
+
React.createElement("span", { style: { fontSize: typography.fontSize.sm, color: "rgba(240,235,223,0.4)", fontFamily: typography.fontFamily.sans } }, user.role))));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { colors, spacing, typography } from "../tokens";
|
|
3
|
+
export const Stat = ({ value, label, style }) => (React.createElement("div", { style: Object.assign({ display: "flex", flexDirection: "column", gap: spacing[4] }, style) },
|
|
4
|
+
React.createElement("span", { style: {
|
|
5
|
+
fontSize: typography.fontSize["4xl"],
|
|
6
|
+
fontWeight: typography.fontWeight.bold,
|
|
7
|
+
color: colors.neutral.white,
|
|
8
|
+
fontFamily: typography.fontFamily.sans,
|
|
9
|
+
lineHeight: 1,
|
|
10
|
+
} }, value),
|
|
11
|
+
React.createElement("span", { style: {
|
|
12
|
+
fontSize: typography.fontSize.base,
|
|
13
|
+
color: "rgba(240,235,223,0.5)",
|
|
14
|
+
fontFamily: typography.fontFamily.sans,
|
|
15
|
+
} }, label)));
|
|
16
|
+
export default Stat;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from "./tokens";
|
|
2
|
+
export { Button } from "./components/Button";
|
|
3
|
+
export type { ButtonProps, ButtonVariant, ButtonSize } from "./components/Button";
|
|
4
|
+
export { Badge } from "./components/Badge";
|
|
5
|
+
export type { BadgeProps, BadgeVariant } from "./components/Badge";
|
|
6
|
+
export { Input, Select } from "./components/Input";
|
|
7
|
+
export type { InputProps, SelectProps } from "./components/Input";
|
|
8
|
+
export { CardFeature, CardTrail, CardPricing } from "./components/Card";
|
|
9
|
+
export type { CardFeatureProps, CardTrailProps, CardPricingProps } from "./components/Card";
|
|
10
|
+
export { Checkbox, Radio, Toggle } from "./components/FormControls";
|
|
11
|
+
export type { CheckboxProps, RadioProps, ToggleProps } from "./components/FormControls";
|
|
12
|
+
export { Header, Tabs, Breadcrumb, Sidebar } from "./components/Nav";
|
|
13
|
+
export type { HeaderProps, TabsProps, BreadcrumbProps, SidebarProps, NavItem, SidebarItem } from "./components/Nav";
|
|
14
|
+
export { Stat } from "./components/Stat";
|
|
15
|
+
export type { StatProps } from "./components/Stat";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// @laje/design-system — Entry Point
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
// Tokens
|
|
5
|
+
export * from "./tokens";
|
|
6
|
+
// Components
|
|
7
|
+
export { Button } from "./components/Button";
|
|
8
|
+
export { Badge } from "./components/Badge";
|
|
9
|
+
export { Input, Select } from "./components/Input";
|
|
10
|
+
export { CardFeature, CardTrail, CardPricing } from "./components/Card";
|
|
11
|
+
export { Checkbox, Radio, Toggle } from "./components/FormControls";
|
|
12
|
+
export { Header, Tabs, Breadcrumb, Sidebar } from "./components/Nav";
|
|
13
|
+
export { Stat } from "./components/Stat";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const colors: {
|
|
2
|
+
readonly brand: {
|
|
3
|
+
readonly primary: "#6600FF";
|
|
4
|
+
readonly secondary: "#0ACCE8";
|
|
5
|
+
readonly accentPink: "#F060A8";
|
|
6
|
+
readonly accentOrange: "#E02A0A";
|
|
7
|
+
};
|
|
8
|
+
readonly neutral: {
|
|
9
|
+
readonly white: "#F0EBDF";
|
|
10
|
+
readonly black: "#0F0B1A";
|
|
11
|
+
readonly dark: "#0F0B1A";
|
|
12
|
+
};
|
|
13
|
+
readonly purple: {
|
|
14
|
+
readonly 100: "#E0CCFF";
|
|
15
|
+
readonly 200: "#BF99FF";
|
|
16
|
+
readonly 300: "#9960FA";
|
|
17
|
+
readonly 400: "#7B52F0";
|
|
18
|
+
readonly 500: "#6600FF";
|
|
19
|
+
readonly 600: "#4200A8";
|
|
20
|
+
};
|
|
21
|
+
readonly cyan: {
|
|
22
|
+
readonly 100: "#C7F7FF";
|
|
23
|
+
readonly 300: "#66E5F7";
|
|
24
|
+
readonly 500: "#0ACCE8";
|
|
25
|
+
readonly 700: "#058496";
|
|
26
|
+
};
|
|
27
|
+
readonly semantic: {
|
|
28
|
+
readonly success: "#21C45E";
|
|
29
|
+
readonly warning: "#F2B300";
|
|
30
|
+
readonly error: "#ED2E2E";
|
|
31
|
+
readonly info: "#2185F2";
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export declare const spacing: {
|
|
35
|
+
readonly 2: "2px";
|
|
36
|
+
readonly 4: "4px";
|
|
37
|
+
readonly 8: "8px";
|
|
38
|
+
readonly 12: "12px";
|
|
39
|
+
readonly 16: "16px";
|
|
40
|
+
readonly 24: "24px";
|
|
41
|
+
readonly 32: "32px";
|
|
42
|
+
readonly 40: "40px";
|
|
43
|
+
readonly 48: "48px";
|
|
44
|
+
readonly 64: "64px";
|
|
45
|
+
readonly 80: "80px";
|
|
46
|
+
readonly 96: "96px";
|
|
47
|
+
readonly 128: "128px";
|
|
48
|
+
};
|
|
49
|
+
export declare const radius: {
|
|
50
|
+
readonly none: "0px";
|
|
51
|
+
readonly sm: "4px";
|
|
52
|
+
readonly md: "8px";
|
|
53
|
+
readonly lg: "16px";
|
|
54
|
+
readonly xl: "24px";
|
|
55
|
+
readonly full: "999px";
|
|
56
|
+
};
|
|
57
|
+
export declare const typography: {
|
|
58
|
+
readonly fontFamily: {
|
|
59
|
+
readonly sans: "'Inter', 'Helvetica Neue', Arial, sans-serif";
|
|
60
|
+
readonly display: "'Inter', 'Helvetica Neue', Arial, sans-serif";
|
|
61
|
+
};
|
|
62
|
+
readonly fontSize: {
|
|
63
|
+
readonly xs: "11px";
|
|
64
|
+
readonly sm: "12px";
|
|
65
|
+
readonly base: "14px";
|
|
66
|
+
readonly md: "16px";
|
|
67
|
+
readonly lg: "18px";
|
|
68
|
+
readonly xl: "24px";
|
|
69
|
+
readonly "2xl": "28px";
|
|
70
|
+
readonly "3xl": "36px";
|
|
71
|
+
readonly "4xl": "48px";
|
|
72
|
+
readonly "5xl": "64px";
|
|
73
|
+
readonly display: "96px";
|
|
74
|
+
};
|
|
75
|
+
readonly fontWeight: {
|
|
76
|
+
readonly regular: 400;
|
|
77
|
+
readonly medium: 500;
|
|
78
|
+
readonly semibold: 600;
|
|
79
|
+
readonly bold: 700;
|
|
80
|
+
};
|
|
81
|
+
readonly lineHeight: {
|
|
82
|
+
readonly tight: 1.2;
|
|
83
|
+
readonly normal: 1.5;
|
|
84
|
+
readonly relaxed: 1.75;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
export declare const cssVars: string;
|
|
88
|
+
export type Colors = typeof colors;
|
|
89
|
+
export type Spacing = typeof spacing;
|
|
90
|
+
export type Radius = typeof radius;
|
|
91
|
+
export type Typography = typeof typography;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// LAJE Design System — Tokens
|
|
3
|
+
// Gerado automaticamente a partir do Figma Design System LAJE
|
|
4
|
+
// figma.com/design/2hgeRrANocH98PpwwXldg7
|
|
5
|
+
// ─────────────────────────────────────────────────────────────
|
|
6
|
+
// ── Helpers ──────────────────────────────────────────────────
|
|
7
|
+
function rgbToHex(r, g, b) {
|
|
8
|
+
const toHex = (n) => Math.round(n * 255).toString(16).padStart(2, "0");
|
|
9
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
10
|
+
}
|
|
11
|
+
// ── Colors ────────────────────────────────────────────────────
|
|
12
|
+
export const colors = {
|
|
13
|
+
brand: {
|
|
14
|
+
primary: "#6600FF", // purple/500
|
|
15
|
+
secondary: "#0ACCE8", // cyan/500
|
|
16
|
+
accentPink: "#F060A8",
|
|
17
|
+
accentOrange: "#E02A0A",
|
|
18
|
+
},
|
|
19
|
+
neutral: {
|
|
20
|
+
white: "#F0EBDF",
|
|
21
|
+
black: "#0F0B1A",
|
|
22
|
+
dark: "#0F0B1A",
|
|
23
|
+
},
|
|
24
|
+
purple: {
|
|
25
|
+
100: "#E0CCFF",
|
|
26
|
+
200: "#BF99FF",
|
|
27
|
+
300: "#9960FA",
|
|
28
|
+
400: "#7B52F0",
|
|
29
|
+
500: "#6600FF",
|
|
30
|
+
600: "#4200A8",
|
|
31
|
+
},
|
|
32
|
+
cyan: {
|
|
33
|
+
100: "#C7F7FF",
|
|
34
|
+
300: "#66E5F7",
|
|
35
|
+
500: "#0ACCE8",
|
|
36
|
+
700: "#058496",
|
|
37
|
+
},
|
|
38
|
+
semantic: {
|
|
39
|
+
success: "#21C45E",
|
|
40
|
+
warning: "#F2B300",
|
|
41
|
+
error: "#ED2E2E",
|
|
42
|
+
info: "#2185F2",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
// ── Spacing ───────────────────────────────────────────────────
|
|
46
|
+
export const spacing = {
|
|
47
|
+
2: "2px",
|
|
48
|
+
4: "4px",
|
|
49
|
+
8: "8px",
|
|
50
|
+
12: "12px",
|
|
51
|
+
16: "16px",
|
|
52
|
+
24: "24px",
|
|
53
|
+
32: "32px",
|
|
54
|
+
40: "40px",
|
|
55
|
+
48: "48px",
|
|
56
|
+
64: "64px",
|
|
57
|
+
80: "80px",
|
|
58
|
+
96: "96px",
|
|
59
|
+
128: "128px",
|
|
60
|
+
};
|
|
61
|
+
// ── Border Radius ─────────────────────────────────────────────
|
|
62
|
+
export const radius = {
|
|
63
|
+
none: "0px",
|
|
64
|
+
sm: "4px",
|
|
65
|
+
md: "8px",
|
|
66
|
+
lg: "16px",
|
|
67
|
+
xl: "24px",
|
|
68
|
+
full: "999px",
|
|
69
|
+
};
|
|
70
|
+
// ── Typography ────────────────────────────────────────────────
|
|
71
|
+
export const typography = {
|
|
72
|
+
fontFamily: {
|
|
73
|
+
sans: "'Inter', 'Helvetica Neue', Arial, sans-serif",
|
|
74
|
+
display: "'Inter', 'Helvetica Neue', Arial, sans-serif",
|
|
75
|
+
},
|
|
76
|
+
fontSize: {
|
|
77
|
+
xs: "11px",
|
|
78
|
+
sm: "12px",
|
|
79
|
+
base: "14px",
|
|
80
|
+
md: "16px",
|
|
81
|
+
lg: "18px",
|
|
82
|
+
xl: "24px",
|
|
83
|
+
"2xl": "28px",
|
|
84
|
+
"3xl": "36px",
|
|
85
|
+
"4xl": "48px",
|
|
86
|
+
"5xl": "64px",
|
|
87
|
+
display: "96px",
|
|
88
|
+
},
|
|
89
|
+
fontWeight: {
|
|
90
|
+
regular: 400,
|
|
91
|
+
medium: 500,
|
|
92
|
+
semibold: 600,
|
|
93
|
+
bold: 700,
|
|
94
|
+
},
|
|
95
|
+
lineHeight: {
|
|
96
|
+
tight: 1.2,
|
|
97
|
+
normal: 1.5,
|
|
98
|
+
relaxed: 1.75,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
// ── CSS Variables (para uso direto em CSS/Tailwind) ───────────
|
|
102
|
+
export const cssVars = `
|
|
103
|
+
/* LAJE Design System — CSS Variables */
|
|
104
|
+
|
|
105
|
+
/* Brand */
|
|
106
|
+
--laje-brand-primary: ${colors.brand.primary};
|
|
107
|
+
--laje-brand-secondary: ${colors.brand.secondary};
|
|
108
|
+
--laje-brand-accent-pink: ${colors.brand.accentPink};
|
|
109
|
+
--laje-brand-accent-orange: ${colors.brand.accentOrange};
|
|
110
|
+
|
|
111
|
+
/* Neutral */
|
|
112
|
+
--laje-neutral-white: ${colors.neutral.white};
|
|
113
|
+
--laje-neutral-black: ${colors.neutral.black};
|
|
114
|
+
|
|
115
|
+
/* Purple */
|
|
116
|
+
--laje-purple-100: ${colors.purple[100]};
|
|
117
|
+
--laje-purple-200: ${colors.purple[200]};
|
|
118
|
+
--laje-purple-300: ${colors.purple[300]};
|
|
119
|
+
--laje-purple-400: ${colors.purple[400]};
|
|
120
|
+
--laje-purple-500: ${colors.purple[500]};
|
|
121
|
+
--laje-purple-600: ${colors.purple[600]};
|
|
122
|
+
|
|
123
|
+
/* Cyan */
|
|
124
|
+
--laje-cyan-100: ${colors.cyan[100]};
|
|
125
|
+
--laje-cyan-300: ${colors.cyan[300]};
|
|
126
|
+
--laje-cyan-500: ${colors.cyan[500]};
|
|
127
|
+
--laje-cyan-700: ${colors.cyan[700]};
|
|
128
|
+
|
|
129
|
+
/* Semantic */
|
|
130
|
+
--laje-success: ${colors.semantic.success};
|
|
131
|
+
--laje-warning: ${colors.semantic.warning};
|
|
132
|
+
--laje-error: ${colors.semantic.error};
|
|
133
|
+
--laje-info: ${colors.semantic.info};
|
|
134
|
+
|
|
135
|
+
/* Spacing */
|
|
136
|
+
--laje-space-2: ${spacing[2]};
|
|
137
|
+
--laje-space-4: ${spacing[4]};
|
|
138
|
+
--laje-space-8: ${spacing[8]};
|
|
139
|
+
--laje-space-12: ${spacing[12]};
|
|
140
|
+
--laje-space-16: ${spacing[16]};
|
|
141
|
+
--laje-space-24: ${spacing[24]};
|
|
142
|
+
--laje-space-32: ${spacing[32]};
|
|
143
|
+
--laje-space-40: ${spacing[40]};
|
|
144
|
+
--laje-space-48: ${spacing[48]};
|
|
145
|
+
--laje-space-64: ${spacing[64]};
|
|
146
|
+
--laje-space-80: ${spacing[80]};
|
|
147
|
+
--laje-space-96: ${spacing[96]};
|
|
148
|
+
--laje-space-128: ${spacing[128]};
|
|
149
|
+
|
|
150
|
+
/* Radius */
|
|
151
|
+
--laje-radius-none: ${radius.none};
|
|
152
|
+
--laje-radius-sm: ${radius.sm};
|
|
153
|
+
--laje-radius-md: ${radius.md};
|
|
154
|
+
--laje-radius-lg: ${radius.lg};
|
|
155
|
+
--laje-radius-xl: ${radius.xl};
|
|
156
|
+
--laje-radius-full: ${radius.full};
|
|
157
|
+
`;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@laje/design-system",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Design System oficial da LAJE — tokens, componentes React e estilos CSS",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.esm.js",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./tokens": "./dist/tokens/index.js",
|
|
15
|
+
"./styles": "./dist/styles/laje.css"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc && echo 'Build complete'",
|
|
22
|
+
"dev": "tsc --watch"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": ">=17.0.0",
|
|
26
|
+
"react-dom": ">=17.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/react": "^18.3.30",
|
|
30
|
+
"@types/react-dom": "^18.3.7",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"laje",
|
|
35
|
+
"design-system",
|
|
36
|
+
"react",
|
|
37
|
+
"components",
|
|
38
|
+
"tokens"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT"
|
|
41
|
+
}
|