@rojaostudio/ds-core 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/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/chunk-MJA5ZGMI.js +405 -0
- package/dist/chunk-NIIC6HPN.js +898 -0
- package/dist/chunk-ORMEWXMH.js +33 -0
- package/dist/chunk-S5GNBSEX.js +21 -0
- package/dist/generate.d.ts +86 -0
- package/dist/generate.js +3 -0
- package/dist/generateTheme-Cz0jvLRW.d.ts +31 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/recipe.schema-kfS2fk61.d.ts +165 -0
- package/dist/recipes.d.ts +25 -0
- package/dist/recipes.js +2 -0
- package/dist/themes.d.ts +36 -0
- package/dist/themes.js +193 -0
- package/dist/tokens.d.ts +492 -0
- package/dist/tokens.js +2 -0
- package/package.json +80 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __objRest = (source, exclude) => {
|
|
21
|
+
var target = {};
|
|
22
|
+
for (var prop in source)
|
|
23
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
+
target[prop] = source[prop];
|
|
25
|
+
if (source != null && __getOwnPropSymbols)
|
|
26
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
+
target[prop] = source[prop];
|
|
29
|
+
}
|
|
30
|
+
return target;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { __objRest, __spreadProps, __spreadValues };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// recipes/index.ts
|
|
2
|
+
var rojao = {
|
|
3
|
+
name: "rojao",
|
|
4
|
+
description: "Brand Roj\xE3o (loja). Neutro preto + accent green, Inter.",
|
|
5
|
+
brand: {
|
|
6
|
+
primary: "neutral-900",
|
|
7
|
+
hover: "neutral-700",
|
|
8
|
+
onPrimary: "white",
|
|
9
|
+
secondary: "white",
|
|
10
|
+
onSecondary: "neutral-900",
|
|
11
|
+
accent: "green-500",
|
|
12
|
+
accentLight: "green-50",
|
|
13
|
+
onAccent: "white"
|
|
14
|
+
},
|
|
15
|
+
surface: "neutral",
|
|
16
|
+
text: "neutral",
|
|
17
|
+
fonts: { body: "inter", display: "inter" }
|
|
18
|
+
};
|
|
19
|
+
var recipes = { rojao };
|
|
20
|
+
|
|
21
|
+
export { recipes, rojao };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export { G as GenResult, R as ResolvedTheme, T as TokenMap, g as generateTheme, r as resolveTheme } from './generateTheme-Cz0jvLRW.js';
|
|
2
|
+
import { a as BrandDef } from './recipe.schema-kfS2fk61.js';
|
|
3
|
+
export { A as Archetype, B as BrandColors, C as ColorRef, D as DarkStrategy, F as FontDef, P as PaletteRef, S as ScaleToken, T as TextStyle, b as TextStyleName, c as TypeScale } from './recipe.schema-kfS2fk61.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* emitCss.ts — formata o output do generateTheme num theme.css legível.
|
|
7
|
+
* Header + bloco light (.theme-<name>) + bloco dark (seletor duplo), agrupado
|
|
8
|
+
* por seção e alinhado. Os themes/*.css passam a ser GERADOS por isto. #12.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
declare function emitCss(def: BrandDef): string;
|
|
12
|
+
|
|
13
|
+
type ClaudeMdTarget = "claude" | "cursor" | "agents";
|
|
14
|
+
interface ClaudeMdOptions {
|
|
15
|
+
/** URL do CSS hospedado (DS-5 serving). Default: ds.rojao.ai/themes/<slug>.css */
|
|
16
|
+
cssUrl?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function emitClaudeMd(def: BrandDef, opts?: ClaudeMdOptions): string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* scale.ts — deriva uma escala 50–900 a partir de UMA cor custom (1 hex). #28.
|
|
22
|
+
*
|
|
23
|
+
* O motor (generateTheme) resolve cor por step(palette, n) → var(--color-<p>-<n>),
|
|
24
|
+
* o que exige uma paleta com escala pronta. Pra cor de marca arbitrária — o
|
|
25
|
+
* lojista no Taiq, ou cor livre no ds.rojao.ai — não há paleta curada: esta
|
|
26
|
+
* função deriva a escala mantendo o matiz e cravando a cor ORIGINAL no step de
|
|
27
|
+
* lightness mais próximo, pra o usuário sempre enxergar a SUA cor no resultado.
|
|
28
|
+
*
|
|
29
|
+
* Curva em OKLCH (espaço perceptual, como Tailwind v4/Radix): L por step + croma
|
|
30
|
+
* modulado (menos nos claros pra não lavar, cheio nos médios-escuros pra tom vivo)
|
|
31
|
+
* + redução de croma ao gamut sRGB. Tons escuros preservam croma (sem marrom-lama
|
|
32
|
+
* do HSL antigo). Quentes (amarelo/âmbar) giram o matiz pro laranja ao escurecer —
|
|
33
|
+
* como o Material — porque amarelo-escuro-vibrante não existe no sRGB.
|
|
34
|
+
*
|
|
35
|
+
* Semente: taiq/lib/palette.ts (buildPalette, 5 slots) expandido p/ 10 steps,
|
|
36
|
+
* mais os helpers de contraste WCAG (on-color legível sobre cor saturada).
|
|
37
|
+
*/
|
|
38
|
+
type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
39
|
+
type Scale = Record<ScaleStep, string>;
|
|
40
|
+
declare const SCALE_STEPS: ScaleStep[];
|
|
41
|
+
declare function hexToHsl(hex: string): [number, number, number];
|
|
42
|
+
declare function hslToHex(h: number, s: number, l: number): string;
|
|
43
|
+
/**
|
|
44
|
+
* Gera a escala 50–900 de uma cor (OKLCH). O step mais próximo do L de entrada
|
|
45
|
+
* recebe a cor EXATA; os demais fixam o matiz, atingem o L-alvo do step e modulam
|
|
46
|
+
* croma (com redução ao gamut). Quentes giram pro laranja no escuro (warmShift).
|
|
47
|
+
*/
|
|
48
|
+
declare function buildScale(inputHex: string): Scale;
|
|
49
|
+
declare function relativeLuminance(hex: string): number;
|
|
50
|
+
declare function contrastRatio(a: string, b: string): number;
|
|
51
|
+
/**
|
|
52
|
+
* Melhor cor de texto ("on-<color>") sobre um fundo: testa branco, preto e os
|
|
53
|
+
* extremos da própria escala (quando dada) e devolve o de MAIOR contraste —
|
|
54
|
+
* garante legibilidade do texto sobre a cor de marca, inclusive tons médios
|
|
55
|
+
* saturados onde quase-branco ficaria frágil (~3:1).
|
|
56
|
+
*/
|
|
57
|
+
declare function onColor(bgHex: string, scale?: Scale): string;
|
|
58
|
+
/** A ColorRef é um hex literal de 6 dígitos (cor de marca custom)? */
|
|
59
|
+
declare const isHex: (c: string) => boolean;
|
|
60
|
+
/** ColorRef (paleta-step, white/black, #hex) → hex de 6 dígitos, ou null. */
|
|
61
|
+
declare function refToHex(c: string): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Tons de marca derivados de 1 hex, pro motor preencher o que o recipe não
|
|
64
|
+
* trouxe: `hover` (um step mais escuro na escala; se a cor já está no fundo,
|
|
65
|
+
* um mais claro) e `on` (texto de maior contraste sobre a cor).
|
|
66
|
+
*/
|
|
67
|
+
declare function brandTones(hex: string): {
|
|
68
|
+
hover: string;
|
|
69
|
+
on: string;
|
|
70
|
+
};
|
|
71
|
+
type PaletteDefs = Record<string, string | Record<string, string>>;
|
|
72
|
+
/**
|
|
73
|
+
* A marca é "neutral" (P&B) quando a primary é (quase) dessaturada — MEDIDO, não
|
|
74
|
+
* declarado. Primary carvão/preta lê neutral; saturada lê chromatic. Substitui a
|
|
75
|
+
* decisão explícita `archetype: 'neutral'|'chromatic'`. Um `archetype` explícito
|
|
76
|
+
* no recipe ainda vence, como override de borda.
|
|
77
|
+
*/
|
|
78
|
+
declare function isNeutralBrand(def: {
|
|
79
|
+
brand: {
|
|
80
|
+
primary: string;
|
|
81
|
+
};
|
|
82
|
+
archetype?: string;
|
|
83
|
+
palettes?: PaletteDefs;
|
|
84
|
+
}): boolean;
|
|
85
|
+
|
|
86
|
+
export { BrandDef, type ClaudeMdOptions, type ClaudeMdTarget, SCALE_STEPS, type Scale, type ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, refToHex, relativeLuminance };
|
package/dist/generate.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, refToHex, relativeLuminance, resolveTheme } from './chunk-NIIC6HPN.js';
|
|
2
|
+
import './chunk-MJA5ZGMI.js';
|
|
3
|
+
import './chunk-ORMEWXMH.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { a as BrandDef } from './recipe.schema-kfS2fk61.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* generateTheme.ts — motor puro do gerador de tema (Fase 1, issue #12).
|
|
5
|
+
*
|
|
6
|
+
* BrandDef → tokens semânticos (light + dark) por dois eixos neutros:
|
|
7
|
+
* surface (page, default, raised, overlay) vs text (text, border, icon).
|
|
8
|
+
* Caso simples = surface === text. Cobre "neutral" e "chromatic"; "mix" = v2.
|
|
9
|
+
*
|
|
10
|
+
* Dark: bloco só contém vars que MUDAM vs light (CSS cascade). Surfaces/text
|
|
11
|
+
* vêm dos eixos dark (override) ou herdam o light. Bordas dark = white-alpha
|
|
12
|
+
* canônico 8/14/22 (canonicaliza drift de zinc-alpha do taiq).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
type TokenMap = Record<string, string>;
|
|
16
|
+
type GenResult = {
|
|
17
|
+
supported: boolean;
|
|
18
|
+
light: TokenMap;
|
|
19
|
+
dark: TokenMap;
|
|
20
|
+
note?: string;
|
|
21
|
+
};
|
|
22
|
+
declare function generateTheme(def: BrandDef): GenResult;
|
|
23
|
+
interface ResolvedTheme {
|
|
24
|
+
tokens: TokenMap;
|
|
25
|
+
isDark: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare function resolveTheme(def: BrandDef, opts?: {
|
|
28
|
+
mode?: string | null;
|
|
29
|
+
}): ResolvedTheme;
|
|
30
|
+
|
|
31
|
+
export { type GenResult as G, type ResolvedTheme as R, type TokenMap as T, generateTheme as g, resolveTheme as r };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { G as GenResult, R as ResolvedTheme, T as TokenMap, g as generateTheme, r as resolveTheme } from './generateTheme-Cz0jvLRW.js';
|
|
2
|
+
export { ClaudeMdOptions, ClaudeMdTarget, SCALE_STEPS, Scale, ScaleStep, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, refToHex, relativeLuminance } from './generate.js';
|
|
3
|
+
export { A as Archetype, B as BrandColors, a as BrandDef, C as ColorRef, D as DarkStrategy, F as FontDef, P as PaletteRef, S as ScaleToken, T as TextStyle, b as TextStyleName, c as TypeScale } from './recipe.schema-kfS2fk61.js';
|
|
4
|
+
export { primitives, tokens } from './tokens.js';
|
|
5
|
+
export { RecipeName, recipes } from './recipes.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { recipes } from './chunk-S5GNBSEX.js';
|
|
2
|
+
export { SCALE_STEPS, brandTones, buildScale, contrastRatio, emitClaudeMd, emitCss, generateTheme, hexToHsl, hslToHex, isHex, isNeutralBrand, onColor, refToHex, relativeLuminance, resolveTheme } from './chunk-NIIC6HPN.js';
|
|
3
|
+
export { primitives, tokens } from './chunk-MJA5ZGMI.js';
|
|
4
|
+
import './chunk-ORMEWXMH.js';
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recipe.schema.ts — contrato de input do gerador de tema (Fase 1).
|
|
3
|
+
*
|
|
4
|
+
* Um BrandDef + as regras de derivação da base.css produzem deterministicamente
|
|
5
|
+
* um theme.css. Os 6 temas atuais (styles/themes/*.css) passam a ser OUTPUT
|
|
6
|
+
* destes BrandDef. Ver docs/fase1-gerador-tema.md e issue #12.
|
|
7
|
+
*
|
|
8
|
+
* Princípio: o BrandDef captura só DECISÃO DE MARCA. Tudo que é fórmula
|
|
9
|
+
* (surfaces, text, borders, soft-states) o gerador deriva por tabela do arquétipo.
|
|
10
|
+
*/
|
|
11
|
+
/** Referência a cor: "<palette>-<step>" | keyword | hex | expressão color-mix literal. */
|
|
12
|
+
type ColorRef = string;
|
|
13
|
+
/** Paleta primitiva (escala 50–900) disponível em tokens/index.ts. */
|
|
14
|
+
type PaletteRef = "neutral" | "zinc" | "cream" | "coal" | "red" | "green" | "blue" | "teal" | "amber" | "purple" | "orange" | "cobalt" | "brick" | "moss" | "clay" | "flare" | "ash" | "ember" | "navy";
|
|
15
|
+
type Archetype = "neutral" | "chromatic";
|
|
16
|
+
type DarkStrategy = "invert" | "lighten" | "mix";
|
|
17
|
+
type FontDef = {
|
|
18
|
+
/** nome da var sem prefixo: "inter" → var(--font-inter). */
|
|
19
|
+
body: string;
|
|
20
|
+
display?: string;
|
|
21
|
+
editorial?: string;
|
|
22
|
+
mono?: string;
|
|
23
|
+
/** fallback do display. default: "sans". */
|
|
24
|
+
displayKind?: "sans" | "serif";
|
|
25
|
+
};
|
|
26
|
+
/** Text styles semânticos editáveis (espelham os --font-size-* da base.css). */
|
|
27
|
+
type TextStyleName = "caption" | "label" | "body" | "button-sm" | "button-md" | "button-lg" | "heading-sm" | "heading-md" | "heading-lg";
|
|
28
|
+
/** Steps da escala de tamanho primitiva (o ramp). O único lugar com px cru. */
|
|
29
|
+
type ScaleToken = "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
|
|
30
|
+
/** Override de um text style: aponta pra um token da escala (nunca px solto). */
|
|
31
|
+
type TextStyle = {
|
|
32
|
+
token?: ScaleToken;
|
|
33
|
+
};
|
|
34
|
+
/** Type scale per-brand — cada text style referencia um token de tamanho. */
|
|
35
|
+
type TypeScale = Partial<Record<TextStyleName, TextStyle>>;
|
|
36
|
+
/** Arredondamento do tema. "soft" = default da base (8px); "pill" = 9999; "square" = 0. */
|
|
37
|
+
type RadiusScale = "sharp" | "soft" | "round" | "pill" | "square";
|
|
38
|
+
/** Radius por papel — controle/botão, card/superfície e avatar variam independente. */
|
|
39
|
+
type RadiusByRole = {
|
|
40
|
+
control?: RadiusScale;
|
|
41
|
+
card?: RadiusScale;
|
|
42
|
+
avatar?: RadiusScale;
|
|
43
|
+
};
|
|
44
|
+
/** Voz do display (nome/títulos) além da fonte — uppercase, tracking. */
|
|
45
|
+
type DisplayStyle = {
|
|
46
|
+
transform?: "uppercase";
|
|
47
|
+
tracking?: "wide";
|
|
48
|
+
};
|
|
49
|
+
/** Textura sutil no fundo da vitrine (--surface-texture). Ausente/'none' = limpo. */
|
|
50
|
+
type SurfacePattern = "dots" | "grain" | "rays" | "soft-blobs" | "none";
|
|
51
|
+
/** Estilo dos cards de produto/destaque — sombra, proporção da imagem, overlay de texto. */
|
|
52
|
+
type CardStyle = {
|
|
53
|
+
elevation?: "flat" | "sm" | "md";
|
|
54
|
+
imageRatio?: "square" | "portrait" | "landscape";
|
|
55
|
+
overlay?: boolean;
|
|
56
|
+
};
|
|
57
|
+
/** Tratamento do hero da vitrine — o maior diferencial visual entre arquétipos. */
|
|
58
|
+
type HeroStyle = {
|
|
59
|
+
/** estrutura do hero; lida pelo consumidor como data-hero. 'flat' = banner atual. */
|
|
60
|
+
treatment?: "flat" | "overlay" | "medallion" | "split";
|
|
61
|
+
/** moldura do logo no hero; data-logo-frame no consumidor. */
|
|
62
|
+
logoFrame?: "none" | "badge" | "plate";
|
|
63
|
+
/** intensidade do scrim sobre a foto do hero (%) → --hero-overlay. */
|
|
64
|
+
overlay?: number;
|
|
65
|
+
};
|
|
66
|
+
/** Template estrutural da vitrine. 'classic'/ausente = composição atual byte a byte. */
|
|
67
|
+
type LayoutArchetype = "classic" | "editorial" | "compact" | "menu";
|
|
68
|
+
/** Escala completa de uma paleta: step (50…900) → hex. */
|
|
69
|
+
type PaletteScale = Record<string, string>;
|
|
70
|
+
type BrandColors = {
|
|
71
|
+
primary: ColorRef;
|
|
72
|
+
hover?: ColorRef;
|
|
73
|
+
onPrimary?: ColorRef;
|
|
74
|
+
secondary?: ColorRef;
|
|
75
|
+
onSecondary?: ColorRef;
|
|
76
|
+
accent?: ColorRef;
|
|
77
|
+
accentLight?: ColorRef;
|
|
78
|
+
onAccent?: ColorRef;
|
|
79
|
+
borderFocus?: ColorRef;
|
|
80
|
+
};
|
|
81
|
+
type BrandDef = {
|
|
82
|
+
/** → seletor .theme-<name> */
|
|
83
|
+
name: string;
|
|
84
|
+
description?: string;
|
|
85
|
+
/**
|
|
86
|
+
* OPCIONAL — override da natureza derivada (isNeutralBrand mede a saturação da
|
|
87
|
+
* primary). Omita: o motor mede. Cuidado: o valor não afeta SÓ o focus ring —
|
|
88
|
+
* 'neutral' também dispara a INVERSÃO da brand no dark (primary→branco). Use só
|
|
89
|
+
* pra forçar um caso de borda (pastel dessaturado-mas-colorido) que a medição erra.
|
|
90
|
+
*/
|
|
91
|
+
archetype?: Archetype;
|
|
92
|
+
/** "ref" (usa paletas existentes) | "mix" (color-mix sobre âncoras → mixTheme). default "ref". */
|
|
93
|
+
scaleMode?: "ref" | "mix";
|
|
94
|
+
/** permite cor literal (hex/keyword cru) no output — só p/ mono puro (studio). */
|
|
95
|
+
allowLiteral?: boolean;
|
|
96
|
+
/** opt-in (%): mistura um traço da brand-primary nas superfícies (page/default/
|
|
97
|
+
* raised) — a marca pinta o ambiente, não só os botões. Off (curados) = neutro. */
|
|
98
|
+
surfaceTint?: number;
|
|
99
|
+
/** opt-in (%): aura radial da brand no topo do fundo (--surface-gradient). 0/ausente
|
|
100
|
+
* = sem gradiente. ~22 = assinatura padrão; boutique=0; café atenuado (#341). */
|
|
101
|
+
surfaceGradient?: number;
|
|
102
|
+
/** opt-in: primary clara demais p/ texto branco → escurece o fill pro mesmo matiz
|
|
103
|
+
* mais profundo (branco legível e brandado, em vez de texto preto mecânico). */
|
|
104
|
+
darkenFill?: boolean;
|
|
105
|
+
brand: BrandColors;
|
|
106
|
+
/**
|
|
107
|
+
* Paletas custom da marca. Cada valor é:
|
|
108
|
+
* - um hex (#rrggbb) → o motor deriva a escala 50–900 (buildScale, #28), ou
|
|
109
|
+
* - uma escala completa { "50": "#…", …, "900": "#…" } → emitida como-está
|
|
110
|
+
* (usada na migração das paletas legadas, que têm curva curada à mão).
|
|
111
|
+
* Emite --color-<nome>-<step>. NÃO fazem parte do core — cada marca tem as suas.
|
|
112
|
+
*/
|
|
113
|
+
palettes?: Record<string, string | PaletteScale>;
|
|
114
|
+
/**
|
|
115
|
+
* Type scale per-brand — overrides parciais sobre os text styles semânticos
|
|
116
|
+
* da base (--font-size-*). Ausente → herda o default da base.css. v1: só size.
|
|
117
|
+
* Emite só o que foi customizado, então recipes sem `type` ficam diff-clean.
|
|
118
|
+
*/
|
|
119
|
+
type?: TypeScale;
|
|
120
|
+
/**
|
|
121
|
+
* Dois eixos neutros independentes (o padrão real do sistema):
|
|
122
|
+
* - surface: page / default / raised. (overlay usa o text-900.)
|
|
123
|
+
* - text: text-* / border-* / icon.
|
|
124
|
+
* Caso simples: surface === text. laudax: surface=cream, text=neutral.
|
|
125
|
+
* taiq: surface=zinc(light)/coal(dark), text=zinc. (substitui o antigo surfaceTint.)
|
|
126
|
+
*/
|
|
127
|
+
surface: PaletteRef;
|
|
128
|
+
text: PaletteRef;
|
|
129
|
+
fonts: FontDef;
|
|
130
|
+
/** Voz do display (nome/títulos): uppercase + tracking. Ausente → herda a base. */
|
|
131
|
+
display?: DisplayStyle;
|
|
132
|
+
/**
|
|
133
|
+
* Arredondamento. Aceita um nível único (aplica a tudo) OU por papel
|
|
134
|
+
* ({ control, card, avatar }). Ausente/"soft" → herda a base (8px), diff-clean.
|
|
135
|
+
* Emite --radius-control/card/avatar/tooltip/toast. O badge é sempre pill.
|
|
136
|
+
*/
|
|
137
|
+
radius?: RadiusScale | RadiusByRole;
|
|
138
|
+
/** Textura do fundo da vitrine → --surface-texture (+ --surface-texture-size). */
|
|
139
|
+
surfacePattern?: SurfacePattern;
|
|
140
|
+
/** Estilo dos cards → --card-shadow / --card-image-ratio / --card-overlay. */
|
|
141
|
+
card?: CardStyle;
|
|
142
|
+
/** Tratamento do hero → --hero-overlay (valor) + data-hero/data-logo-frame (estrutura). */
|
|
143
|
+
hero?: HeroStyle;
|
|
144
|
+
/** Template estrutural da vitrine → data-template no consumidor. */
|
|
145
|
+
layout?: LayoutArchetype;
|
|
146
|
+
dark?: {
|
|
147
|
+
/** default decorre do arquétipo (neutral→invert, chromatic→lighten, mix→mix). */
|
|
148
|
+
strategy?: DarkStrategy;
|
|
149
|
+
/** overrides explícitos do brand no dark (ex: chromatic clareado p/ teal-400). */
|
|
150
|
+
brand?: Partial<BrandColors>;
|
|
151
|
+
/** eixos no dark — default = os do light. ex: laudax cream→neutral, taiq zinc→coal. */
|
|
152
|
+
surface?: PaletteRef;
|
|
153
|
+
text?: PaletteRef;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Tokens de componente específicos (shell, choice-card, selection, tooltip…).
|
|
157
|
+
* v1 NÃO gera — passthrough/patch. Hoje só o taiq usa pesado. Ver issue #12 (v2).
|
|
158
|
+
*/
|
|
159
|
+
extras?: Record<string, {
|
|
160
|
+
light?: string;
|
|
161
|
+
dark?: string;
|
|
162
|
+
}>;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type { Archetype as A, BrandColors as B, ColorRef as C, DarkStrategy as D, FontDef as F, PaletteRef as P, ScaleToken as S, TextStyle as T, BrandDef as a, TextStyleName as b, TypeScale as c };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { a as BrandDef } from './recipe.schema-kfS2fk61.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* recipes/index.ts — as receitas PÚBLICAS do design system.
|
|
5
|
+
*
|
|
6
|
+
* SPEC EXECUTÁVEL: generateTheme(def) deve reproduzir cada styles/themes/<name>.css.
|
|
7
|
+
* Harness: pnpm validate:themes.
|
|
8
|
+
*
|
|
9
|
+
* As receitas de marca de produto e de cliente NÃO vivem aqui: uma receita é a cor da
|
|
10
|
+
* marca, e publicá-la num registry público vaza o mesmo que publicar o tema. Elas ficam
|
|
11
|
+
* em `private/brands.ts`, fora do `files[]` e fora do repositório público.
|
|
12
|
+
* Ver rojao-ds#101.
|
|
13
|
+
*
|
|
14
|
+
* O catálogo de presets genéricos (nicho → paleta) continua público, em `themes/index.ts`:
|
|
15
|
+
* ele não descreve marca de ninguém, descreve ponto de partida.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** rojao — neutro preto + accent green, Inter. */
|
|
19
|
+
declare const rojao: BrandDef;
|
|
20
|
+
declare const recipes: {
|
|
21
|
+
readonly rojao: BrandDef;
|
|
22
|
+
};
|
|
23
|
+
type RecipeName = keyof typeof recipes;
|
|
24
|
+
|
|
25
|
+
export { type RecipeName, recipes, rojao };
|
package/dist/recipes.js
ADDED
package/dist/themes.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { a as BrandDef } from './recipe.schema-kfS2fk61.js';
|
|
2
|
+
import { T as TokenMap } from './generateTheme-Cz0jvLRW.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* themes/index.ts — catálogo de temas de NICHO (vitrine/Linkpage de lojistas).
|
|
6
|
+
*
|
|
7
|
+
* Temas que um lojista escolhe por nicho (padaria, barber…), consumidos em RUNTIME
|
|
8
|
+
* via generateTheme (tokens inline na vitrine), NÃO como classes .theme-* estáticas.
|
|
9
|
+
* Cada tema = recipe (BrandDef: cor + accent + surface + surfaceTint + fonte +
|
|
10
|
+
* display + radius por papel) + mode claro/escuro FIXO. `tier` é negócio do consumidor.
|
|
11
|
+
*
|
|
12
|
+
* Onda 1: cor de marca + accent + surface ambientada + serif/sans.
|
|
13
|
+
* Onda 2 (#341): fontes Jost/Nunito, display.transform (uppercase), radius por papel
|
|
14
|
+
* (avatar/card/control + pill/square). Curadoria a partir do protótipo aprovado.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
type ThemeMode = "light" | "dark";
|
|
18
|
+
type NicheTheme = {
|
|
19
|
+
id: string;
|
|
20
|
+
skin: string;
|
|
21
|
+
name: string;
|
|
22
|
+
niche: string;
|
|
23
|
+
mode: ThemeMode;
|
|
24
|
+
recipe: BrandDef;
|
|
25
|
+
};
|
|
26
|
+
declare const NICHE_THEMES: NicheTheme[];
|
|
27
|
+
/** id do tema neutro default por modo — fallback quando a loja não escolheu nenhum. */
|
|
28
|
+
declare const DEFAULT_THEME_ID: Record<ThemeMode, string>;
|
|
29
|
+
declare function getNicheTheme(id: string): NicheTheme | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Tokens finais do tema, conforme o `mode` FIXO. generateTheme devolve `dark` como
|
|
32
|
+
* DIFF sobre o light; pra um tema escuro fundimos os dois. O consumidor injeta inline.
|
|
33
|
+
*/
|
|
34
|
+
declare function resolveThemeTokens(theme: NicheTheme): TokenMap;
|
|
35
|
+
|
|
36
|
+
export { DEFAULT_THEME_ID, NICHE_THEMES, type NicheTheme, type ThemeMode, getNicheTheme, resolveThemeTokens };
|
package/dist/themes.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { generateTheme } from './chunk-NIIC6HPN.js';
|
|
2
|
+
import './chunk-MJA5ZGMI.js';
|
|
3
|
+
import { __spreadValues, __objRest } from './chunk-ORMEWXMH.js';
|
|
4
|
+
|
|
5
|
+
// themes/index.ts
|
|
6
|
+
function niche(skin, name, nicheLabel, mode, recipe) {
|
|
7
|
+
const _a = recipe, { surface = "neutral" } = _a, rest = __objRest(_a, ["surface"]);
|
|
8
|
+
return {
|
|
9
|
+
id: `theme-${skin}`,
|
|
10
|
+
skin,
|
|
11
|
+
name,
|
|
12
|
+
niche: nicheLabel,
|
|
13
|
+
mode,
|
|
14
|
+
recipe: __spreadValues({ name: skin, surface, text: "neutral" }, rest)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
var serif = { body: "inter", display: "instrument-serif", displayKind: "serif" };
|
|
18
|
+
var sans = { body: "inter", display: "bricolage", displayKind: "sans" };
|
|
19
|
+
var UPPER = { transform: "uppercase" };
|
|
20
|
+
var EDITORIAL = { layout: "editorial", card: { elevation: "flat", imageRatio: "landscape" }, hero: { treatment: "medallion", logoFrame: "plate" } };
|
|
21
|
+
var PUNCH = { card: { elevation: "md", imageRatio: "square", overlay: true }, hero: { treatment: "overlay", logoFrame: "badge", overlay: 55 } };
|
|
22
|
+
var BOUTIQUE = { layout: "compact", card: { elevation: "flat", imageRatio: "portrait" }, hero: { treatment: "flat", logoFrame: "none" } };
|
|
23
|
+
var MENU = { layout: "menu", card: { elevation: "sm", imageRatio: "square" }, hero: { treatment: "overlay", logoFrame: "badge", overlay: 50 } };
|
|
24
|
+
var NICHE_THEMES = [
|
|
25
|
+
// Neutros — default/backfill quando a loja não escolheu tema.
|
|
26
|
+
niche("neutro-claro", "Neutro Claro", "Geral", "light", { brand: { primary: "#0a0a0a", accent: "#2563eb", secondary: "#525252" }, fonts: sans, radius: "soft" }),
|
|
27
|
+
niche("neutro-escuro", "Neutro Escuro", "Geral", "dark", { brand: { primary: "#0a0a0a", accent: "#3b82f6", secondary: "#525252" }, fonts: sans, radius: "soft", surface: "coal" }),
|
|
28
|
+
// Nichos — cor/accent/surface/fonte/display/radius/gradiente do protótipo.
|
|
29
|
+
niche("padaria", "Aconchego", "Padaria", "light", __spreadValues({
|
|
30
|
+
brand: { primary: "#d97706", accent: "#be123c", secondary: "#92400e" },
|
|
31
|
+
surface: "cream",
|
|
32
|
+
surfaceGradient: 22,
|
|
33
|
+
surfacePattern: "grain",
|
|
34
|
+
fonts: serif,
|
|
35
|
+
radius: "soft"
|
|
36
|
+
}, EDITORIAL)),
|
|
37
|
+
niche("doceria", "Confeito", "Doceria & Bolos", "light", __spreadValues({
|
|
38
|
+
brand: { primary: "#db2777", accent: "#f59e0b", secondary: "#9d174d" },
|
|
39
|
+
surfaceTint: 8,
|
|
40
|
+
surfaceGradient: 22,
|
|
41
|
+
surfacePattern: "soft-blobs",
|
|
42
|
+
fonts: serif,
|
|
43
|
+
radius: { avatar: "pill", card: "round", control: "pill" }
|
|
44
|
+
}, EDITORIAL)),
|
|
45
|
+
niche("estetica", "Lumi\xE8re", "Est\xE9tica & Beleza", "light", __spreadValues({
|
|
46
|
+
brand: { primary: "#b76e79", accent: "#c98b94", secondary: "#7c5560" },
|
|
47
|
+
surfaceTint: 8,
|
|
48
|
+
surfaceGradient: 22,
|
|
49
|
+
surfacePattern: "soft-blobs",
|
|
50
|
+
fonts: { body: "jost", display: "instrument-serif", displayKind: "serif" },
|
|
51
|
+
radius: { avatar: "pill", card: "round", control: "pill" }
|
|
52
|
+
}, EDITORIAL)),
|
|
53
|
+
niche("cafe", "Torra", "Cafeteria", "light", __spreadValues({
|
|
54
|
+
brand: { primary: "#6f4e37", accent: "#a87c4f", secondary: "#4a3526" },
|
|
55
|
+
surface: "cream",
|
|
56
|
+
surfaceTint: 6,
|
|
57
|
+
surfaceGradient: 12,
|
|
58
|
+
surfacePattern: "grain",
|
|
59
|
+
fonts: serif,
|
|
60
|
+
radius: "soft"
|
|
61
|
+
}, EDITORIAL)),
|
|
62
|
+
niche("pet", "Patinhas", "Pet shop", "light", __spreadValues({
|
|
63
|
+
brand: { primary: "#14a3a3", accent: "#ff7a59", secondary: "#0e7490" },
|
|
64
|
+
surfaceTint: 8,
|
|
65
|
+
surfaceGradient: 22,
|
|
66
|
+
surfacePattern: "soft-blobs",
|
|
67
|
+
fonts: { body: "nunito", display: "nunito", displayKind: "sans" },
|
|
68
|
+
radius: { avatar: "pill", card: "round", control: "round" }
|
|
69
|
+
}, BOUTIQUE)),
|
|
70
|
+
niche("boutique", "Atelier", "Moda & Boutique", "light", __spreadValues({
|
|
71
|
+
brand: { primary: "#111111", accent: "#0d9488", secondary: "#57534e" },
|
|
72
|
+
fonts: { body: "jost", display: "jost", displayKind: "sans" },
|
|
73
|
+
display: { transform: "uppercase", tracking: "wide" },
|
|
74
|
+
radius: "square"
|
|
75
|
+
}, BOUTIQUE)),
|
|
76
|
+
niche("barber", "Brotherhood", "Barbearia", "dark", __spreadValues({
|
|
77
|
+
brand: { primary: "#c9a45c", accent: "#b91c1c", secondary: "#8a6d3b" },
|
|
78
|
+
surface: "coal",
|
|
79
|
+
surfaceGradient: 18,
|
|
80
|
+
surfacePattern: "rays",
|
|
81
|
+
fonts: sans,
|
|
82
|
+
display: UPPER,
|
|
83
|
+
radius: "soft"
|
|
84
|
+
}, PUNCH)),
|
|
85
|
+
niche("burger", "Smash", "Hamburgueria", "dark", __spreadValues({
|
|
86
|
+
brand: { primary: "#ff3b30", accent: "#ffb703", secondary: "#b91c1c" },
|
|
87
|
+
surface: "coal",
|
|
88
|
+
surfaceTint: 6,
|
|
89
|
+
surfaceGradient: 18,
|
|
90
|
+
surfacePattern: "rays",
|
|
91
|
+
fonts: sans,
|
|
92
|
+
display: UPPER,
|
|
93
|
+
radius: { avatar: "soft", card: "round", control: "pill" }
|
|
94
|
+
}, MENU)),
|
|
95
|
+
// PILOTO de tema PREMIUM bespoke (skin layer no taiq). Identidade de FOGO: brand
|
|
96
|
+
// vermelho-fogo + accent laranja sobre coal, display punchy (Bricolage) caixa-alta,
|
|
97
|
+
// rays no fundo e hero overlay forte (scrim 60). O DS continua AGNÓSTICO: gera só os
|
|
98
|
+
// tokens; a arte/CSS de hambúrguer (gradiente quente, glow, slot de ilustração) mora
|
|
99
|
+
// em taiq/lib/themes/premium-skins.ts, injetada por cima quando a loja usa esta skin.
|
|
100
|
+
niche("brasa-pop", "Brasa Pop", "Hamburgueria", "dark", __spreadValues({
|
|
101
|
+
brand: { primary: "#dc2626", accent: "#f97316", secondary: "#991b1b" },
|
|
102
|
+
surface: "coal",
|
|
103
|
+
surfaceTint: 8,
|
|
104
|
+
surfaceGradient: 24,
|
|
105
|
+
surfacePattern: "rays",
|
|
106
|
+
fonts: { body: "inter", display: "bricolage", displayKind: "sans" },
|
|
107
|
+
display: UPPER,
|
|
108
|
+
radius: { avatar: "soft", card: "round", control: "pill" },
|
|
109
|
+
darkenFill: true
|
|
110
|
+
}, PUNCH)),
|
|
111
|
+
niche("academia", "Forja", "Academia & CrossFit", "dark", __spreadValues({
|
|
112
|
+
brand: { primary: "#c6ff00", accent: "#06b6d4", secondary: "#4d7c0f" },
|
|
113
|
+
surface: "coal",
|
|
114
|
+
surfaceGradient: 18,
|
|
115
|
+
surfacePattern: "rays",
|
|
116
|
+
fonts: sans,
|
|
117
|
+
display: UPPER,
|
|
118
|
+
radius: { avatar: "square", card: "sharp", control: "square" }
|
|
119
|
+
}, PUNCH)),
|
|
120
|
+
// #350 Fase 5: tattoo era #d22b2b (vermelho) e colidia com o burger #ff3b30 —
|
|
121
|
+
// "dois vermelhos dark+coal+uppercase". Vira MONO ("Black Ink" fiel ao nome): primary
|
|
122
|
+
// neutra → no dark a marca inverte pra branco sobre coal. Distinta do burger por
|
|
123
|
+
// cor (mono×vermelho) e dos neutros por serif+uppercase+grain+sharp.
|
|
124
|
+
niche("tattoo", "Black Ink", "Est\xFAdio de tatuagem", "dark", __spreadValues({
|
|
125
|
+
brand: { primary: "#0a0a0a", accent: "#e11d48", secondary: "#525252" },
|
|
126
|
+
surface: "coal",
|
|
127
|
+
surfaceGradient: 18,
|
|
128
|
+
surfacePattern: "grain",
|
|
129
|
+
fonts: serif,
|
|
130
|
+
display: UPPER,
|
|
131
|
+
radius: { avatar: "square", card: "sharp", control: "sharp" }
|
|
132
|
+
}, PUNCH)),
|
|
133
|
+
// #350 — nichos de comida/delivery (arquétipo MENU): o burger não podia ser o único.
|
|
134
|
+
// Cores espaçadas em matiz por modo pra passar o lint de catálogo (sem colisão).
|
|
135
|
+
niche("hotdog", "Dog Club", "Hot dog & Food truck", "dark", __spreadValues({
|
|
136
|
+
brand: { primary: "#2563eb", accent: "#facc15", secondary: "#1e40af" },
|
|
137
|
+
surface: "coal",
|
|
138
|
+
surfaceTint: 5,
|
|
139
|
+
surfaceGradient: 18,
|
|
140
|
+
surfacePattern: "rays",
|
|
141
|
+
fonts: sans,
|
|
142
|
+
display: UPPER,
|
|
143
|
+
radius: { avatar: "soft", card: "round", control: "pill" }
|
|
144
|
+
}, MENU)),
|
|
145
|
+
niche("ramen", "Nori Club", "Ramen & Street food", "dark", __spreadValues({
|
|
146
|
+
brand: { primary: "#16a34a", accent: "#facc15", secondary: "#15803d" },
|
|
147
|
+
surface: "coal",
|
|
148
|
+
surfaceTint: 5,
|
|
149
|
+
surfaceGradient: 18,
|
|
150
|
+
surfacePattern: "rays",
|
|
151
|
+
fonts: sans,
|
|
152
|
+
display: UPPER,
|
|
153
|
+
radius: { avatar: "soft", card: "round", control: "pill" }
|
|
154
|
+
}, MENU)),
|
|
155
|
+
niche("pizzaria", "Sapore", "Pizzaria", "light", __spreadValues({
|
|
156
|
+
brand: { primary: "#d62828", accent: "#0a7d33", secondary: "#9d1717" },
|
|
157
|
+
surface: "cream",
|
|
158
|
+
surfaceTint: 5,
|
|
159
|
+
surfaceGradient: 18,
|
|
160
|
+
surfacePattern: "grain",
|
|
161
|
+
fonts: serif,
|
|
162
|
+
radius: "soft"
|
|
163
|
+
}, MENU)),
|
|
164
|
+
niche("sorveteria", "Gelato", "Sorveteria & A\xE7a\xED", "light", __spreadValues({
|
|
165
|
+
brand: { primary: "#7c3aed", accent: "#f472b6", secondary: "#5b21b6" },
|
|
166
|
+
surfaceTint: 8,
|
|
167
|
+
surfaceGradient: 22,
|
|
168
|
+
surfacePattern: "soft-blobs",
|
|
169
|
+
fonts: { body: "nunito", display: "nunito", displayKind: "sans" },
|
|
170
|
+
radius: { avatar: "pill", card: "round", control: "round" }
|
|
171
|
+
}, MENU)),
|
|
172
|
+
niche("floricultura", "Verdejante", "Floricultura", "light", __spreadValues({
|
|
173
|
+
brand: { primary: "#4d7c4d", accent: "#d98cb3", secondary: "#3d633d" },
|
|
174
|
+
surfaceTint: 6,
|
|
175
|
+
surfaceGradient: 18,
|
|
176
|
+
surfacePattern: "soft-blobs",
|
|
177
|
+
fonts: serif,
|
|
178
|
+
radius: { avatar: "pill", card: "round", control: "soft" }
|
|
179
|
+
}, EDITORIAL))
|
|
180
|
+
];
|
|
181
|
+
var DEFAULT_THEME_ID = {
|
|
182
|
+
light: "theme-neutro-claro",
|
|
183
|
+
dark: "theme-neutro-escuro"
|
|
184
|
+
};
|
|
185
|
+
function getNicheTheme(id) {
|
|
186
|
+
return NICHE_THEMES.find((t) => t.id === id);
|
|
187
|
+
}
|
|
188
|
+
function resolveThemeTokens(theme) {
|
|
189
|
+
const { light, dark } = generateTheme(theme.recipe);
|
|
190
|
+
return theme.mode === "dark" ? __spreadValues(__spreadValues({}, light), dark) : light;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { DEFAULT_THEME_ID, NICHE_THEMES, getNicheTheme, resolveThemeTokens };
|