> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-pt/guias-do-contribuidor/padroes-de-web-ui/styling-and-theming.md).

# Estilização e Temas

Esta página cobre padrões abrangentes de estilo para UIs web do Decentraland usando styled-components com a solução de estilo do Material UI.

{% hint style="info" %}
Todos os exemplos de código neste documento são ilustrativos. Eles não representam componentes de produção, mas demonstram padrões e normas.
{% endhint %}

## Princípios Centrais

1. **Somente sintaxe de objeto** - Use notação de objeto para forte suporte ao TypeScript
2. **Tema em primeiro lugar** - Todos os valores vêm do tema UI2
3. **Sem estilos inline** - Use componentes estilizados para todo o estilo
4. **Tudo tipado** - Aproveite o TypeScript para props e tema
5. **Responsivo por padrão** - Use breakpoints do tema

***

## Padrão de Sintaxe de Objeto

Componentes UI2 **DEVE** usar a sintaxe de objeto. Isso garante forte suporte ao TypeScript, validação csstype e integração direta com o tema.

{% hint style="warning" %}
A sintaxe de template literal é **não permitida**. Sempre use notação de objeto.
{% endhint %}

### Exemplo Básico

```tsx
// ✅ Bom: Sintaxe de objeto
const Button = styled('button')({
  color: 'turquoise',
  padding: '8px 16px',
});

// ❌ Ruim: Sintaxe de template literal
const Button = styled.button`
  color: turquoise;
  padding: 8px 16px;
`;
```

### Com Tema e Props

```tsx
interface ButtonProps {
  primary?: boolean;
  size?: 'small' | 'medium' | 'large';
}

// ✅ Bom: Sintaxe de objeto com tema e props
const Button = styled('button')<ButtonProps>(({ theme, primary, size = 'medium' }) => ({
  color: primary ? theme.palette.primary.main : theme.palette.text.primary,
  backgroundColor: primary ? theme.palette.primary.main : 'transparent',
  borderRadius: theme.shape.borderRadius,
  padding: {
    small: theme.spacing(0.5, 1),
    medium: theme.spacing(1, 2),
    large: theme.spacing(1.5, 3),
  }[size],
}));
```

{% hint style="danger" %}
**Regra**: Os valores DEVEM sempre vir do tema UI2. Códigos hexadecimais arbitrários ou valores em pixels não são permitidos.
{% endhint %}

***

## Sintaxe de Elemento

Ao estilizar elementos HTML nativos, sempre use a forma de chamada de função `styled('tag')`.

### Sintaxe Correta

```tsx
// ✅ Bom: Forma de chamada de função
const Container = styled('div')({
  display: 'flex',
  flexDirection: 'column',
});

const Action = styled('button')(({ theme }) => ({
  color: theme.palette.primary.main,
  padding: theme.spacing(1, 2),
}));

const Label = styled('label')(({ theme }) => ({
  color: theme.palette.text.secondary,
  fontSize: theme.typography.caption.fontSize,
}));
```

### Sintaxe Incorreta

```tsx
// ❌ Ruim: Forma de propriedade (sintaxe legada)
const Container = styled.div`
  display: flex;
  flex-direction: column;
`;

const Action = styled.button`
  color: ${props => props.theme.palette.primary.main};
`;
```

***

## Sem Estilos Inline

Estilos inline (`style={...}`) **NÃO DEVEM** ser usados em componentes UI2.

### Por que não usar estilos inline?

* Ignoram a tipagem do tema
* Mais difícil de manter
* Impedem reutilização
* Não podem ser otimizados
* Sem validação do TypeScript

### A Maneira Correta

```tsx
// ❌ Ruim: Estilos inline
<Card 
  key={id} 
  style={{ backgroundColor: color } as React.CSSProperties}
>
  <CardHeader>
    <Title style={{ fontSize: 20 }}>{title}</Title>
  </CardHeader>
</Card>

// ✅ Bom: Componentes estilizados com props
interface CardProps {
  backgroundColor: string;
}

interface TitleProps {
  size: number;
}

const StyledCard = styled('div')<CardProps>(({ backgroundColor, theme }) => ({
  backgroundColor,
  borderRadius: theme.shape.borderRadius,
  padding: theme.spacing(2),
}));

const Title = styled('h2')<TitleProps>(({ size, theme }) => ({
  fontSize: theme.typography.pxToRem(size),
  color: theme.palette.text.primary,
}));

<StyledCard key={id} backgroundColor={color}>
  <CardHeader>
    <Title size={20}>{title}</Title>
  </CardHeader>
</StyledCard>
```

***

## Breakpoints

Use `theme.breakpoints` helpers em vez de valores de pixel codificados.

### Helpers de Breakpoint

| Helper                | Uso                                     | Descrição               |
| --------------------- | --------------------------------------- | ----------------------- |
| `up(key)`             | `theme.breakpoints.up('md')`            | Min-width e acima       |
| `down(key)`           | `theme.breakpoints.down('md')`          | Max-width e abaixo      |
| `between(start, end)` | `theme.breakpoints.between('sm', 'lg')` | Entre dois breakpoints  |
| `only(key)`           | `theme.breakpoints.only('md')`          | Apenas neste breakpoint |

### Exemplos

```tsx
// ❌ Ruim: Breakpoints codificados
const Panel = styled('div')`
  @media (max-width: 768px) {
    width: 100%;
  }
`;

// ✅ Bom: Breakpoints do tema
interface PanelProps {
  expanded: boolean;
}

const Panel = styled('div')<PanelProps>(({ theme, expanded }) => ({
  width: expanded ? '400px' : '0',
  transition: theme.transitions.create('width'),
  
  [theme.breakpoints.down('sm')]: {
    width: expanded ? '100%' : '0',
  },
}));
```

### Múltiplos Breakpoints

```tsx
const Layout = styled('div')(({ theme }) => ({
  display: 'grid',
  gridTemplateColumns: '1fr 320px',
  gap: theme.spacing(2),
  
  // Mobile: coluna única
  [theme.breakpoints.down('md')]: {
    gridTemplateColumns: '1fr',
  },
  
  // Desktop grande: sidebar mais larga
  [theme.breakpoints.up('xl')]: {
    gridTemplateColumns: '1fr 400px',
  },
  
  // Faixa de tablet: gap diferente
  [theme.breakpoints.between('sm', 'lg')]: {
    gap: theme.spacing(3),
  },
  
  // Apenas tablet
  [theme.breakpoints.only('md')]: {
    padding: theme.spacing(2),
  },
}));
```

***

## Escala de Espaçamento

Use `theme.spacing` exclusivamente para margens, paddings e gaps.

### Convenção de Espaçamento

* `theme.spacing(n)` onde `n` é um número
* A unidade base é tipicamente 8px
* `spacing(1)` = 8px, `spacing(2)` = 16px, etc.
* Decimais permitidos: `spacing(1.5)` = 12px

```tsx
// ✅ Bom: Espaçamento do tema
const Box = styled('div')(({ theme }) => ({
  padding: theme.spacing(2),           // 16px
  margin: theme.spacing(1, 0),         // 8px vertical, 0 horizontal
  gap: theme.spacing(1.5),             // 12px
  paddingInline: theme.spacing(3),     // 24px esquerda/direita
}));

// ❌ Ruim: Valores brutos em pixels
const Box = styled('div')({
  padding: '16px',
  margin: '8px 0',
  gap: '12px',
});
```

### Padrões Comuns de Espaçamento

```tsx
const Card = styled('div')(({ theme }) => ({
  // Espaçamento uniforme ao redor
  padding: theme.spacing(3),
  
  // Vertical/horizontal diferente
  padding: theme.spacing(2, 3),  // 16px vertical, 24px horizontal
  
  // Todos os lados diferentes
  padding: theme.spacing(1, 2, 3, 2),  // top, right, bottom, left
  
  // Propriedades lógicas (preferidas para suporte RTL)
  paddingBlock: theme.spacing(2),      // topo e fundo
  paddingInline: theme.spacing(3),     // esquerda e direita
  marginBlockStart: theme.spacing(1),  // margin-top
}));
```

***

## Z-Index e Empilhamento

Siga a escala de z-index do tema. Nunca use valores arbitrários de z-index.

### Valores de Z-Index do Tema

```tsx
theme.zIndex.mobileStepper  // 1000
theme.zIndex.fab            // 1050
theme.zIndex.speedDial      // 1050
theme.zIndex.appBar         // 1100
theme.zIndex.drawer         // 1200
theme.zIndex.modal          // 1300
theme.zIndex.snackbar       // 1400
theme.zIndex.tooltip        // 1500
```

### Uso Correto

```tsx
// ✅ Bom: z-index do tema
const StickyBar = styled('div')(({ theme }) => ({
  position: 'sticky',
  top: 0,
  zIndex: theme.zIndex.appBar,
  backgroundColor: theme.palette.background.paper,
}));

const Overlay = styled('div')(({ theme }) => ({
  position: 'fixed',
  inset: 0,
  zIndex: theme.zIndex.modal,
  backgroundColor: 'rgba(0, 0, 0, 0.5)',
}));

// ❌ Ruim: z-index arbitrário
const StickyBar = styled('div')({
  position: 'sticky',
  top: 0,
  zIndex: 999,  // Não faça isso
});
```

### Melhores Práticas de Contexto de Empilhamento

* Esteja consciente ao criar novos contextos de empilhamento
* Evite `position: relative` desnecessário em elementos pai
* Documente por que um z-index é necessário
* Se precisar de uma nova camada, adicione-a primeiro ao tema

***

## Tokens de Cor

Sempre consuma cores de `theme.palette` e `dclColors`. Sem valores hex ad-hoc.

### Estrutura da Paleta

```tsx
// Cores de texto
theme.palette.text.primary
theme.palette.text.secondary
theme.palette.text.disabled

// Cores de fundo
theme.palette.background.default
theme.palette.background.paper

// Primary/Secondary/Error
theme.palette.primary.main
theme.palette.primary.light
theme.palette.primary.dark
theme.palette.primary.contrastText

// Cores de ação
theme.palette.action.active
theme.palette.action.hover
theme.palette.action.selected
theme.palette.action.disabled
theme.palette.action.disabledBackground

// Dividers
theme.palette.divider
```

### Cores Decentraland

```tsx
import { dclColors } from 'decentraland-ui2';

// Cores de raridade
dclColors.rarity.unique
dclColors.rarity.mythic
dclColors.rarity.legendary
dclColors.rarity.epic
dclColors.rarity.rare
dclColors.rarity.uncommon
dclColors.rarity.common
```

### Exemplos

```tsx
// ✅ Bom: Cores do tema
const Chip = styled('span')(({ theme }) => ({
  color: theme.palette.text.secondary,
  backgroundColor: theme.palette.background.paper,
  borderColor: theme.palette.divider,
  
  '&:hover': {
    backgroundColor: theme.palette.action.hover,
  },
}));

const RarityBadge = styled('span')<{ rarity: string }>(({ rarity }) => ({
  backgroundColor: dclColors.rarity[rarity],
  color: '#FFFFFF', // Cor de contraste - aceitável se documentado
  padding: '4px 8px',
  borderRadius: '4px',
}));

// ❌ Ruim: Valores hexadecimais arbitrários
const Chip = styled('span')({
  color: '#666666',
  backgroundColor: '#FFFFFF',
  borderColor: '#E0E0E0',
});
```

***

## Estados Interativos

Todos os controles interativos DEVEM mostrar estados visíveis para hover, focus, active e disabled.

### Componente Interativo Completo

```tsx
const InteractiveButton = styled('button')(({ theme }) => ({
  // Estado base
  padding: theme.spacing(1, 2),
  backgroundColor: theme.palette.primary.main,
  color: theme.palette.primary.contrastText,
  border: 'none',
  borderRadius: theme.shape.borderRadius,
  cursor: 'pointer',
  outline: 'none',
  transition: theme.transitions.create([
    'background-color',
    'transform',
    'box-shadow',
  ]),
  
  // Estado hover (mouse)
  '&:hover': {
    backgroundColor: theme.palette.primary.dark,
  },
  
  // Estado focus (navegação por teclado)
  '&:focus-visible': {
    outline: `2px solid ${theme.palette.primary.main}`,
    outlineOffset: 2,
    boxShadow: theme.shadows[2],
  },
  
  // Estado ativo/pressionado
  '&:active': {
    backgroundColor: theme.palette.primary.dark,
    transform: 'scale(0.98)',
  },
  
  // Estado desabilitado
  '&:disabled': {
    backgroundColor: theme.palette.action.disabledBackground,
    color: theme.palette.action.disabled,
    cursor: 'not-allowed',
    transform: 'none',
  },
}));
```

### Padrão Focus-Visible

Sempre use `:focus-visible` em vez de `:focus` para evitar mostrar anéis de foco em cliques do mouse:

```tsx
// ✅ Bom: Mostra anel de foco apenas para teclado
'&:focus-visible': {
  outline: `2px solid ${theme.palette.primary.main}`,
  outlineOffset: 2,
}

// ❌ Ruim: Mostra anel de foco em todo clique
'&:focus': {
  outline: `2px solid ${theme.palette.primary.main}`,
}
```

***

## Tipografia

Use variantes de tipografia do tema em vez de propriedades de fonte personalizadas.

```tsx
// ✅ Bom: Variantes de tipografia
const Heading = styled('h1')(({ theme }) => ({
  ...theme.typography.h1,
  marginBottom: theme.spacing(2),
}));

const Body = styled('p')(({ theme}) => ({
  ...theme.typography.body1,
  color: theme.palette.text.secondary,
}));

// Com o componente MUI Typography (preferido)
<Typography variant="h1">Heading</Typography>
<Typography variant="body1">Body text</Typography>

// ❌ Ruim: Tipografia personalizada
const Heading = styled('h1')({
  fontSize: '32px',
  fontWeight: 700,
  lineHeight: 1.2,
});
```

***

## Otimização de Performance

### Não criar Styled Components na renderização

```tsx
// ❌ Ruim: Cria novo componente a cada render
function Component({ color }) {
  const Box = styled('div')({
    backgroundColor: color,
  });
  return <Box />;
}

// ✅ Bom: Criar uma vez, passar props
const Box = styled('div')<{ color: string }>(({ color }) => ({
  backgroundColor: color,
}));

function Component({ color }) {
  return <Box color={color} />;
}
```

### Memoizar Props Derivadas

```tsx
// ❌ Ruim: Cria novo objeto a cada render
<StyledComponent style={{ color: isDark ? 'white' : 'black' }} />

// ✅ Bom: Passar props primitivas
const StyledComponent = styled('div')<{ isDark: boolean }>(({ isDark, theme }) => ({
  color: isDark ? theme.palette.common.white : theme.palette.common.black,
}));

<StyledComponent isDark={isDark} />
```

***

## Convenções de Nomeação

### Nomes de Componentes

* **PascalCase** para componentes
* Nomes descritivos

```tsx
// ✅ Bons nomes
const UserCard = styled('div')({...});
const PrimaryButton = styled('button')({...});
const NavigationList = styled('ul')({...});

// ❌ Nomes ruins
const card = styled('div')({...});
const btn = styled('button')({...});
const list1 = styled('ul')({...});
```

### Estrutura de Arquivos

```
Component.tsx        # Componente principal
Component.styles.ts  # Componentes estilizados
Component.test.tsx   # Testes
Component.stories.tsx # Storybook
```

***

## Próximos Passos

* Rever [Componentes Personalizados](/contributor/contributor-pt/guias-do-contribuidor/padroes-de-web-ui/custom-components.md) para diretrizes de criação de componentes
* Veja [Guia de Migração](https://github.com/decentraland/docs/blob/main/contributor/contributor-guides/web-ui-standards/broken-reference/README.md) para migrações de UI1 para UI2
* Verificar [Visão Geral do Processo](/contributor/contributor-pt/guias-do-contribuidor/padroes-de-web-ui/process-overview.md) para o fluxo de trabalho completo


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-pt/guias-do-contribuidor/padroes-de-web-ui/styling-and-theming.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
