// components.jsx — UI components in the Casa Lar brand.
// Button, Input, Card, Badge, Stat, Quote, Avatar, ListItem.
// ────────────────────────────────────────────────────────────────
// Button — primary (ink), accent (sol), secondary (outline), ghost
// soft (cream), danger-soft. Plus sizes sm/md/lg/xl and icon-only.
// ────────────────────────────────────────────────────────────────
function Button({ variant = 'primary', size = 'md', children, full, icon, iconRight, square }) {
const sizes = {
sm: { fs: 13, pad: '8px 14px', iconPad: 8 },
md: { fs: 14, pad: '11px 18px', iconPad: 11 },
lg: { fs: 16, pad: '14px 24px', iconPad: 14 },
xl: { fs: 18, pad: '18px 30px', iconPad: 18 },
};
const s = sizes[size];
const styles = {
primary: { bg: 'var(--ink)', color: 'var(--paper)', border: 'none' },
accent: { bg: 'var(--sol)', color: 'var(--ink)', border: 'none' },
secondary: { bg: 'var(--paper)', color: 'var(--ink)', border: '1.5px solid var(--ink)' },
ghost: { bg: 'transparent', color: 'var(--ink)', border: '1.5px solid transparent' },
soft: { bg: 'var(--paper-2)', color: 'var(--ink)', border: '1px solid var(--line)' },
folha: { bg: 'var(--folha)', color: 'var(--paper)', border: 'none' },
ceramica: { bg: 'var(--ceramica)',color: 'var(--paper)', border: 'none' },
};
const v = styles[variant];
return (
{icon && {icon} }
{children}
{iconRight && {iconRight} }
);
}
// ────────────────────────────────────────────────────────────────
// Input — text input with label, helper, optional icon
// ────────────────────────────────────────────────────────────────
function Input({ label, helper, value, placeholder, type = 'text', state = 'default', icon, suffix, multiline }) {
const borderColor = state === 'error' ? 'var(--ceramica)'
: state === 'focus' ? 'var(--ink)'
: 'var(--line-strong)';
return (
{label && (
{label}
)}
{icon && {icon} }
{value || placeholder}
{suffix && {suffix} }
{helper && (
{helper}
)}
);
}
// ────────────────────────────────────────────────────────────────
// Badge — tag/chip
// ────────────────────────────────────────────────────────────────
function Badge({ tone = 'neutral', children, dot, size = 'md' }) {
const map = {
neutral: { bg: 'var(--paper-2)', fg: 'var(--ink)', bd: 'var(--line)' },
sol: { bg: 'color-mix(in oklab, var(--sol) 30%, var(--paper))', fg: 'var(--ink)', bd: 'transparent' },
ceramica: { bg: 'color-mix(in oklab, var(--ceramica) 18%, var(--paper))', fg: 'var(--ceramica)', bd: 'transparent' },
folha: { bg: 'color-mix(in oklab, var(--folha) 18%, var(--paper))', fg: 'var(--folha)', bd: 'transparent' },
ceu: { bg: 'color-mix(in oklab, var(--ceu) 18%, var(--paper))', fg: 'var(--ceu)', bd: 'transparent' },
rosa: { bg: 'color-mix(in oklab, var(--rosa) 30%, var(--paper))', fg: 'var(--ink)', bd: 'transparent' },
ink: { bg: 'var(--ink)', fg: 'var(--paper)', bd: 'transparent' },
};
const v = map[tone];
const sizes = {
sm: { fs: 10.5, pad: '3px 8px', dot: 5 },
md: { fs: 11.5, pad: '4px 9px', dot: 6 },
lg: { fs: 13, pad: '6px 12px', dot: 7 },
};
const s = sizes[size];
return (
{dot && (
)}
{children}
);
}
// ────────────────────────────────────────────────────────────────
// Card — content surface (default, accent-bordered, elevated, dark)
// ────────────────────────────────────────────────────────────────
function Card({ children, accent, variant = 'default', padding = 22, full }) {
const variants = {
default: { bg: 'var(--paper)', color: 'var(--ink)', border: '1px solid var(--line)', shadow: 'none' },
soft: { bg: 'var(--paper-2)', color: 'var(--ink)', border: '1px solid var(--line)', shadow: 'none' },
elevated: { bg: 'var(--paper)', color: 'var(--ink)', border: '1px solid var(--line)', shadow: '0 1px 2px rgba(0,0,0,.04), 0 8px 24px rgba(0,0,0,.06)' },
dark: { bg: 'var(--ink)', color: 'var(--paper)', border: 'none', shadow: 'none' },
};
const v = variants[variant];
return (
{children}
);
}
// ────────────────────────────────────────────────────────────────
// Stat — big number with label
// ────────────────────────────────────────────────────────────────
function Stat({ value, label, suffix, accent }) {
return (
{value}{suffix && {suffix} }
{label}
);
}
// ────────────────────────────────────────────────────────────────
// Quote — editorial pull-quote
// ────────────────────────────────────────────────────────────────
function Quote({ children, author, role }) {
return (
{children}
{author && (
{author}
{role && · {role} }
)}
);
}
// ────────────────────────────────────────────────────────────────
// Avatar — initials circle
// ────────────────────────────────────────────────────────────────
function Avatar({ initials, color, size = 44 }) {
const colors = ['var(--ceu)', 'var(--ceramica)', 'var(--sol)', 'var(--rosa)', 'var(--folha)'];
const c = color || colors[(initials.charCodeAt(0) + initials.charCodeAt(1 % initials.length)) % colors.length];
return (
{initials}
);
}
// ────────────────────────────────────────────────────────────────
// ListItem — row with leading icon/dot, primary, secondary, trailing
// ────────────────────────────────────────────────────────────────
function ListItem({ leading, primary, secondary, trailing, divider }) {
return (
{leading &&
{leading}
}
{primary}
{secondary &&
{secondary}
}
{trailing &&
{trailing}
}
);
}
// ────────────────────────────────────────────────────────────────
// Icons — small inline SVGs (sized via currentColor)
// ────────────────────────────────────────────────────────────────
const Icons = {
Heart: ,
Arrow: ,
Mail: ,
Phone: ,
Pin: ,
Check: ,
User: ,
Pix: ,
Shirt: ,
Gift: ,
Play: ,
Hand: ,
};
// ────────────────────────────────────────────────────────────────
// Showcase artboards
// ────────────────────────────────────────────────────────────────
function ComponentsShowcaseA() {
return (
Componentes · Botões & Campos
{/* Buttons */}
Botões
Doe agora
Quero apoiar
Saiba mais
Ver depois
Cancelar
SM
MD
LG
XL
Confirmado
Cancelar inscrição
{/* Inputs */}
);
}
function ComponentsShowcaseB() {
return (
Componentes · Etiquetas, Cards, Listas
Etiquetas
Em andamento
Acolhido
Voluntária
Padrinho
Urgente
Mantenedor
Aguarda
SM
MD
LG
Cards
Apadrinhamento
Sustente um lugar
R$ 120/mês cobre alimentação e escola.
Transparência · 2025
Lista
}
primary="Maria das Graças"
secondary="Coordenadora pedagógica · desde 2014"
trailing="EQUIPE"
divider />
}
primary="Luiz Renato"
secondary="Educador · turno tarde"
trailing="EQUIPE"
divider />
}
primary="Antonia Berenice"
secondary="Psicóloga · quartas e sextas"
trailing="EQUIPE" />
);
}
function ComponentsShowcaseC() {
return (
Componentes · Estatísticas & Citação
Quando entrei, eu não sabia que aniversário tinha bolo.
Hoje sei que a casa é minha.
);
}
Object.assign(window, {
Button, Input, Badge, Card, Stat, Quote, Avatar, ListItem,
Icons,
ComponentsShowcaseA, ComponentsShowcaseB, ComponentsShowcaseC,
});