@rainersoft/design-tokens 2.0.0 → 2.3.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.
@@ -1,280 +0,0 @@
1
- /**
2
- * @fileoverview Utilitários de Acessibilidade - Verificação de Contraste WCAG
3
- *
4
- * @description
5
- * Utilitários para verificar e garantir conformidade com padrões WCAG AA/AAA
6
- * de contraste de cores. Essas funções são agnósticas e podem ser usadas em
7
- * qualquer ambiente (web, mobile, desktop).
8
- *
9
- * @module tokens/accessibility
10
- * @version 2.0.0
11
- * @author Rainer Teixeira
12
- * @since 1.0.0
13
- */
14
-
15
- /**
16
- * Converte cor hexadecimal para RGB
17
- *
18
- * @param {string} hex - Cor em formato hexadecimal (#RRGGBB ou RRGGBB)
19
- * @returns {Object} Objeto com valores RGB { r, g, b }
20
- *
21
- * @example
22
- * ```typescript
23
- * const rgb = hexToRgb('#0891b2');
24
- * // { r: 8, g: 145, b: 178 }
25
- * ```
26
- */
27
- export function hexToRgb(hex: string): { r: number; g: number; b: number } {
28
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
29
- if (!result) {
30
- throw new Error(`Invalid hex color: ${hex}`);
31
- }
32
- return {
33
- r: parseInt(result[1], 16),
34
- g: parseInt(result[2], 16),
35
- b: parseInt(result[3], 16),
36
- };
37
- }
38
-
39
- /**
40
- * Calcula a luminância relativa de uma cor RGB
41
- *
42
- * @param {number} r - Componente vermelho (0-255)
43
- * @param {number} g - Componente verde (0-255)
44
- * @param {number} b - Componente azul (0-255)
45
- * @returns {number} Luminância relativa (0-1)
46
- *
47
- * @description
48
- * Fórmula baseada na recomendação WCAG 2.1 para cálculo de luminância.
49
- *
50
- * @example
51
- * ```typescript
52
- * const luminance = getLuminance(8, 145, 178);
53
- * // 0.234
54
- * ```
55
- */
56
- export function getLuminance(r: number, g: number, b: number): number {
57
- const [rs, gs, bs] = [r, g, b].map((val) => {
58
- const v = val / 255;
59
- return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
60
- });
61
- return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
62
- }
63
-
64
- /**
65
- * Calcula o contraste entre duas cores
66
- *
67
- * @param {string} color1 - Primeira cor em hexadecimal
68
- * @param {string} color2 - Segunda cor em hexadecimal
69
- * @returns {number} Razão de contraste (1-21)
70
- *
71
- * @description
72
- * Retorna a razão de contraste entre duas cores conforme WCAG 2.1.
73
- * Valores mínimos recomendados:
74
- * - WCAG AA (normal): 4.5:1 para texto normal, 3:1 para texto grande
75
- * - WCAG AAA (melhor): 7:1 para texto normal, 4.5:1 para texto grande
76
- *
77
- * @example
78
- * ```typescript
79
- * const contrast = getContrast('#ffffff', '#000000');
80
- * // 21 (máximo contraste)
81
- *
82
- * const contrast2 = getContrast('#0891b2', '#ffffff');
83
- * // 3.2 (atende WCAG AA para texto grande)
84
- * ```
85
- */
86
- export function getContrast(
87
- color1: string,
88
- color2: string
89
- ): number {
90
- const rgb1 = hexToRgb(color1);
91
- const rgb2 = hexToRgb(color2);
92
-
93
- const lum1 = getLuminance(rgb1.r, rgb1.g, rgb1.b);
94
- const lum2 = getLuminance(rgb2.r, rgb2.g, rgb2.b);
95
-
96
- const lighter = Math.max(lum1, lum2);
97
- const darker = Math.min(lum1, lum2);
98
-
99
- return (lighter + 0.05) / (darker + 0.05);
100
- }
101
-
102
- /**
103
- * Verifica se o contraste atende ao padrão WCAG AA
104
- *
105
- * @param {string} foreground - Cor do texto (hexadecimal)
106
- * @param {string} background - Cor de fundo (hexadecimal)
107
- * @param {boolean} [largeText=false] - Se o texto é grande (>=18pt ou >=14pt bold)
108
- * @returns {boolean} true se atende WCAG AA, false caso contrário
109
- *
110
- * @description
111
- * WCAG AA requer:
112
- * - Texto normal: contraste mínimo de 4.5:1
113
- * - Texto grande: contraste mínimo de 3:1
114
- *
115
- * @example
116
- * ```typescript
117
- * const meetsAA = meetsWCAGAA('#0891b2', '#ffffff', false);
118
- * // true (contraste 3.2:1, mas texto normal precisa 4.5:1)
119
- *
120
- * const meetsAALarge = meetsWCAGAA('#0891b2', '#ffffff', true);
121
- * // true (contraste 3.2:1, texto grande precisa 3:1)
122
- * ```
123
- */
124
- export function meetsWCAGAA(
125
- foreground: string,
126
- background: string,
127
- largeText: boolean = false
128
- ): boolean {
129
- const contrast = getContrast(foreground, background);
130
- return largeText ? contrast >= 3 : contrast >= 4.5;
131
- }
132
-
133
- /**
134
- * Verifica se o contraste atende ao padrão WCAG AAA
135
- *
136
- * @param {string} foreground - Cor do texto (hexadecimal)
137
- * @param {string} background - Cor de fundo (hexadecimal)
138
- * @param {boolean} [largeText=false] - Se o texto é grande (>=18pt ou >=14pt bold)
139
- * @returns {boolean} true se atende WCAG AAA, false caso contrário
140
- *
141
- * @description
142
- * WCAG AAA requer:
143
- * - Texto normal: contraste mínimo de 7:1
144
- * - Texto grande: contraste mínimo de 4.5:1
145
- *
146
- * @example
147
- * ```typescript
148
- * const meetsAAA = meetsWCAGAAA('#000000', '#ffffff', false);
149
- * // true (contraste 21:1)
150
- * ```
151
- */
152
- export function meetsWCAGAAA(
153
- foreground: string,
154
- background: string,
155
- largeText: boolean = false
156
- ): boolean {
157
- const contrast = getContrast(foreground, background);
158
- return largeText ? contrast >= 4.5 : contrast >= 7;
159
- }
160
-
161
- /**
162
- * Retorna informações completas de contraste entre duas cores
163
- *
164
- * @param {string} foreground - Cor do texto (hexadecimal)
165
- * @param {string} background - Cor de fundo (hexadecimal)
166
- * @returns {Object} Objeto com informações de contraste
167
- *
168
- * @example
169
- * ```typescript
170
- * const info = getContrastInfo('#0891b2', '#ffffff');
171
- * // {
172
- * // contrast: 3.2,
173
- * // meetsAA: false,
174
- * // meetsAALarge: true,
175
- * // meetsAAA: false,
176
- * // meetsAAALarge: false,
177
- * // level: 'AA Large'
178
- * // }
179
- * ```
180
- */
181
- export function getContrastInfo(
182
- foreground: string,
183
- background: string
184
- ): {
185
- contrast: number;
186
- meetsAA: boolean;
187
- meetsAALarge: boolean;
188
- meetsAAA: boolean;
189
- meetsAAALarge: boolean;
190
- level: 'Fail' | 'AA Large' | 'AA' | 'AAA Large' | 'AAA';
191
- } {
192
- const contrast = getContrast(foreground, background);
193
- const meetsAA = contrast >= 4.5;
194
- const meetsAALarge = contrast >= 3;
195
- const meetsAAA = contrast >= 7;
196
- const meetsAAALarge = contrast >= 4.5;
197
-
198
- let level: 'Fail' | 'AA Large' | 'AA' | 'AAA Large' | 'AAA' = 'Fail';
199
- if (meetsAAA) {
200
- level = 'AAA';
201
- } else if (meetsAAALarge) {
202
- level = 'AAA Large';
203
- } else if (meetsAA) {
204
- level = 'AA';
205
- } else if (meetsAALarge) {
206
- level = 'AA Large';
207
- }
208
-
209
- return {
210
- contrast,
211
- meetsAA,
212
- meetsAALarge,
213
- meetsAAA,
214
- meetsAAALarge,
215
- level,
216
- };
217
- }
218
-
219
- /**
220
- * Valida se uma combinação de cores atende aos padrões de acessibilidade
221
- *
222
- * @param {string} foreground - Cor do texto (hexadecimal)
223
- * @param {string} background - Cor de fundo (hexadecimal)
224
- * @param {Object} options - Opções de validação
225
- * @param {boolean} [options.requireAAA=false] - Se deve requerer WCAG AAA
226
- * @param {boolean} [options.largeText=false] - Se o texto é grande
227
- * @returns {Object} Resultado da validação
228
- *
229
- * @example
230
- * ```typescript
231
- * const validation = validateContrast('#0891b2', '#ffffff', {
232
- * largeText: true
233
- * });
234
- * // {
235
- * // valid: true,
236
- * // level: 'AA Large',
237
- * // contrast: 3.2,
238
- * // message: 'Contraste válido para texto grande (WCAG AA)'
239
- * // }
240
- * ```
241
- */
242
- export function validateContrast(
243
- foreground: string,
244
- background: string,
245
- options: {
246
- requireAAA?: boolean;
247
- largeText?: boolean;
248
- } = {}
249
- ): {
250
- valid: boolean;
251
- level: string;
252
- contrast: number;
253
- message: string;
254
- } {
255
- const { requireAAA = false, largeText = false } = options;
256
- const info = getContrastInfo(foreground, background);
257
-
258
- let valid = false;
259
- let message = '';
260
-
261
- if (requireAAA) {
262
- valid = largeText ? info.meetsAAALarge : info.meetsAAA;
263
- message = valid
264
- ? `Contraste válido (WCAG AAA${largeText ? ' - Texto Grande' : ''})`
265
- : `Contraste insuficiente para WCAG AAA${largeText ? ' - Texto Grande' : ''}. Requerido: ${largeText ? '4.5:1' : '7:1'}, atual: ${info.contrast.toFixed(2)}:1`;
266
- } else {
267
- valid = largeText ? info.meetsAALarge : info.meetsAA;
268
- message = valid
269
- ? `Contraste válido (WCAG AA${largeText ? ' - Texto Grande' : ''})`
270
- : `Contraste insuficiente para WCAG AA${largeText ? ' - Texto Grande' : ''}. Requerido: ${largeText ? '3:1' : '4.5:1'}, atual: ${info.contrast.toFixed(2)}:1`;
271
- }
272
-
273
- return {
274
- valid,
275
- level: info.level,
276
- contrast: info.contrast,
277
- message,
278
- };
279
- }
280
-
@@ -1,142 +0,0 @@
1
- {
2
- "animations": {
3
- "accordion-down": {
4
- "name": "accordion-down",
5
- "duration": "0.2s",
6
- "timingFunction": "ease-out",
7
- "keyframes": {
8
- "from": {
9
- "height": "0"
10
- },
11
- "to": {
12
- "height": "var(--radix-accordion-content-height)"
13
- }
14
- }
15
- },
16
- "accordion-up": {
17
- "name": "accordion-up",
18
- "duration": "0.2s",
19
- "timingFunction": "ease-out",
20
- "keyframes": {
21
- "from": {
22
- "height": "var(--radix-accordion-content-height)"
23
- },
24
- "to": {
25
- "height": "0"
26
- }
27
- }
28
- },
29
- "slide-in": {
30
- "name": "slide-in",
31
- "duration": "0.3s",
32
- "timingFunction": "ease-out",
33
- "keyframes": {
34
- "0%": {
35
- "transform": "translateY(-10px)",
36
- "opacity": "0"
37
- },
38
- "100%": {
39
- "transform": "translateY(0)",
40
- "opacity": "1"
41
- }
42
- }
43
- },
44
- "fade-in": {
45
- "name": "fade-in",
46
- "duration": "0.5s",
47
- "timingFunction": "ease-in",
48
- "keyframes": {
49
- "0%": {
50
- "opacity": "0"
51
- },
52
- "100%": {
53
- "opacity": "1"
54
- }
55
- }
56
- },
57
- "glitch": {
58
- "name": "glitch",
59
- "duration": "4s",
60
- "timingFunction": "linear",
61
- "iterationCount": "infinite",
62
- "keyframes": {
63
- "0%, 100%": {
64
- "transform": "translate(0)"
65
- },
66
- "10%": {
67
- "transform": "translate(-2px, 2px)"
68
- },
69
- "20%": {
70
- "transform": "translate(2px, -2px)"
71
- },
72
- "30%, 50%, 70%, 90%": {
73
- "transform": "translate(0)"
74
- }
75
- }
76
- },
77
- "neon-pulse": {
78
- "name": "neon-pulse",
79
- "duration": "2s",
80
- "timingFunction": "ease-in-out",
81
- "iterationCount": "infinite",
82
- "direction": "alternate",
83
- "keyframes": {
84
- "0%, 100%": {
85
- "filter": "brightness(1) saturate(1)"
86
- },
87
- "50%": {
88
- "filter": "brightness(1.3) saturate(1.2)"
89
- }
90
- }
91
- },
92
- "flicker": {
93
- "name": "flicker",
94
- "duration": "2s",
95
- "timingFunction": "linear",
96
- "iterationCount": "infinite",
97
- "keyframes": {
98
- "0%, 19.999%, 22%, 62.999%, 64%, 64.999%, 70%, 100%": {
99
- "opacity": "1",
100
- "filter": "brightness(1.2)"
101
- },
102
- "20%, 21.999%, 63%, 63.999%, 65%, 69.999%": {
103
- "opacity": "0.85",
104
- "filter": "brightness(0.9)"
105
- }
106
- }
107
- },
108
- "scan-line": {
109
- "name": "scan-line",
110
- "duration": "4s",
111
- "timingFunction": "linear",
112
- "iterationCount": "infinite",
113
- "keyframes": {
114
- "0%": {
115
- "top": "0"
116
- },
117
- "100%": {
118
- "top": "100%"
119
- }
120
- }
121
- },
122
- "glitch-after": {
123
- "name": "glitch-after",
124
- "duration": "3s",
125
- "timingFunction": "linear",
126
- "iterationCount": "infinite",
127
- "keyframes": {
128
- "0%, 100%": {
129
- "opacity": "0"
130
- },
131
- "5%, 10%": {
132
- "opacity": "0.8",
133
- "transform": "translate(-3px, 3px)"
134
- },
135
- "15%, 95%": {
136
- "opacity": "0"
137
- }
138
- }
139
- }
140
- }
141
- }
142
-
@@ -1,30 +0,0 @@
1
- {
2
- "$schema": "https://json.schemastore.org/theme.json",
3
- "$description": "Responsive breakpoints - Mobile-first approach",
4
- "breakpoints": {
5
- "xs": "0px",
6
- "sm": "640px",
7
- "md": "768px",
8
- "lg": "1024px",
9
- "xl": "1280px",
10
- "2xl": "1536px",
11
- "3xl": "1920px"
12
- },
13
- "container": {
14
- "sm": "640px",
15
- "md": "768px",
16
- "lg": "1024px",
17
- "xl": "1280px",
18
- "2xl": "1536px",
19
- "3xl": "1920px"
20
- },
21
- "mediaQueries": {
22
- "xs": "@media (min-width: 0px)",
23
- "sm": "@media (min-width: 640px)",
24
- "md": "@media (min-width: 768px)",
25
- "lg": "@media (min-width: 1024px)",
26
- "xl": "@media (min-width: 1280px)",
27
- "2xl": "@media (min-width: 1536px)",
28
- "3xl": "@media (min-width: 1920px)"
29
- }
30
- }